动态规划及其优化
__Green_tick__ · · 算法·理论
T_0 方格取数
有一个
有一只小熊想从网格的左上角走到右下角。每一步只能:
- 向上
- 向下
- 向右
移动一格,不能重复经过已走过的方格,也不能越界。\ 小熊会收集所有经过方格中的整数,求这些整数的最大可能和。
限制条件
-
1\leq N,M\leq 10^{3} -
-10^{4}\leq A_{i,j}\leq 10^{4}
定义
-
-10^{4}\leq A_{i,j}\leq 10^{4}
定义
-
f[i-1][j] > 0$ 且 $\neq p
则状态
符号说明
代码
#include <bits/stdc++.h>
using namespace std;
#define var long long
#define ln "\n"
/*Optimization series
#pragma GCC/G++ optimize(1)
#pragma GCC/G++ optimize(2)
#pragma GCC/G++ optimize(3,"Ofast","inline")
*/
const var MaxN=2e5+10;
var n,k,q,dp[110][MaxN];
vector<var> v[MaxN];
void solve(){
memset(dp,-1,sizeof dp);
cin>>n>>k>>q;
for(var i=1;i<=n;i++){
var l;
cin>>l;
v[i].clear();
for(var j=0,x;j<l;j++){
cin>>x;
v[i].push_back(x);
}
}
dp[0][1]=0;
for(var r=1;r<=100;r++){
for(var i=1;i<=n;i++){
var cnt=0;
for(auto u:v[i]){
if(cnt>0){
if(dp[r][u]==-1) dp[r][u]=i;
else if(dp[r][u]!=i) dp[r][u]=0;
cnt--;
}
if(dp[r-1][u]!=-1 && dp[r-1][u]!=i){
cnt=k-1;
}
}
}
}
while(q--){
var r,c;
cin>>r>>c;
cout<<((dp[r][c]==-1)?0:1)<<ln;
}
}
int main(){
ios::sync_with_stdio();
cin.tie();
cout.tie();
var T;
cin>>T;
while(T--){
solve();
}
return 0;
}
T_3 多重背包
题意
有一个容量(即承重量)为
要求选择一些物品放入背包,所选物品的重量之和不能超过背包的容量。
求所选物品的总价值可能达到的最大值。
-
1 \leq N \leq 500 -
1 \leq M \leq 10^5 -
1 \leq W_i \leq M -
1 \leq V_i \leq 10^9 -
1 \leq C_i \leq 10^5 -
做法
思路一
将多重背包转化为 0-1 背包。
时间复杂度
思路二
发现在思路一中采用二进制优化。
物品分组优化方法
将每种物品分成若干组,选择物品时以组为单位而非单个物品。通过合理的分组设计,使得1到
-
5 = 1 + 4 -
6 = 2 + 4 -
7 = 3 + 4 -
8 = 1 + 3 + 4 -
9 = 2 + 3 + 4
对于任意
- 分组数量尽可能少
- 通过组间组合能表示
1 到C_i 的所有整数 - 典型分组方式:按
2^k 序列分组(1, 2, 4, 8,\dots )
设物品
其中
思路三
定义
令 f[i][j] 表示:
- 从前
i种物品中选择若干物品 - 放入容量为
j的背包 - 满足总重量不超过
j时 - 所能达到的最大总价值
初始问题的解即为 f[N][M],其中:
N:物品种类总数M:背包总容量
- 二维状态表示:同时记录物品选择范围和容量限制
- 最优子结构:当前状态由前序状态转移得到
- 最终解定位:解存储在状态矩阵的右下角(
f[N][M])转移
枚举从第
i 种物品中拿几个:f[i][j_{k}]=\max _{0\leq p\leq\min\left(k, C_{i}\right)} f[i-1]\left[j_{k-p}\right]+p\cdot V_{i} 我们发现所有
j-pW_N \bmod{W_i} 均相等。 把f[i][j_{k}]=\max _{0\leq p\leq\min\left(k, C_{i}\right)} f[i-1]\left[j_{k-p}\right]+p\cdot V_{i} 改写成
f[i][j_{k}]=k\cdot V_{i}+\max _{0\leq p\leq\min\left(k, C_{i}\right)} \left(f[i-1]\left[j_{k-p}\right]-(k-p)\cdot V_{i}\right) 把
f[i-1]\left[j_{k-p}\right]-(k-p)\cdot V_{i} 记作A_{i}[k-p] ,上式变成f[i][j_{k}]=k\cdot V_{i}+\max _{0\leq p\leq\min\left(k, C_{i}\right)} A_{i}[k-p] 令
t = k - p ,将上式改写为:f[i][j_{k}]=k\cdot V_{i}+\operatorname*{max}_{\max(0,k-C_{i})\leq t\leq k}A_{i}[t] 计算
f[i][j_k] 转化为计算:\max _{\max (0,k-C_{i})\leq t\leq k}A_{i}[t]
计算
通过对比可见:
- 新增一项
A_{i}[k+1] - 可能减少一项
A_{i}[k-C_{i}] (当k+1-C_i > k-C_i 时)