动态规划重点
1.状态表示
2.动态转移方程
1.01背包
一个旅行者有一个最多能用 m 公斤的背包,现在有 n 件物品,它们的重量分别是它们的价值分别为 。若每种物品只有一件求旅行者能获得最大总价值。
若用dfs or 位运算 爆搜,那是, n≥11就爆了
令一数组 f
状态标识:
f[i][j]:前 i 件物品用 j 公斤的背包
动态转移方程:
分为选i,与不选i
选 i
那我要留w[i]空间,只能用(j-w[i]),我可以得c[i] 所以是f[i-1][j-w[i]]+c[i]
不选 i
那就是f[i-1][j]
那结果是 max(f[i-1][j],f[i-1][j-w[i]]+c[i])
所以代码如下:
for(int i=1;i<=n;++i)
for(int j=0;j<=m;++j){//枚举
f[i][j]=f[i-1][j];//原先是这样
if(j>w[i])//选w[i]你必须能装得下
f[i][j]=max(f[i][j],f[i-1][j-w[i]]+c[i]);
//求最大值
}
时间复杂度
但空间复杂度:
那能不能空间优化一下:
首先,要明白要降 i 这一位
否则 (j-w[i]) ……就不能实现
f[j] : 用 j 公斤能装下的最大价值
那如果就直接把 i 位删了
for(int i=1;i<=n;++i)
for(int j=w[i];j<=m;++j)
f[j]=max(f[j],f[j-w[i]]+c[i]);
那会出现问题
f[j-w[i]] 就被这一次(i)更新过 ,我要的是(i-1)
那我咋样不更新那些我要用的点?
因为 j >(j-w[i]) 所以 f[ (j,m] ] 不会影响 f[j]
所以我把 j 倒过来枚举就ok 了
for(int i=1;i<=n;++i)
for(int j=m;j>=w[i];--j)
f[j]=max(f[j],f[j-w[i]]+c[i]);
稍微解释一下,f[i][j]=f[i-1][j] 这行降维就是 “废话” (f[j]=f[j])
所以直接简写成这样
那看一道最简单的题
就是输入
m,n
w[i] , c[i]
然后01背包就 ok 了
代码就不加注释了
#include <bits/stdc++.h>
using namespace std;
int w[207],c[207],f[1007];
int main(){
int m,n;
cin>>m>>n;
for(int i=1;i<=n;++i)cin>>w[i]>>c[i];
for(int i=1;i<=n;++i)
for(int j=m;j>=w[i];--j)
f[j]=max(f[j],f[j-w[i]]+c[i]);
cout<<f[m];
return 0;
}
因为 w[i],c[i] 是“一次性的”
把w[],c[] 优化掉 就是
#include <bits/stdc++.h>
using namespace std;
int w,c,f[1007];
int main(){
int m,n;
cin>>m>>n;
for(int i=1;i<=n;++i){
cin>>w>>c;
for(int j=m;j>=w;--j)
f[j]=max(f[j],f[j-w]+c);
}
cout<<f[m];
return 0;
}
2.完全背包
状态表示:
f[i][j]:前 i 件物品用 j 公斤的背包
分为两种方法去理解
1.定义理解法
分为又选i,与不选i了
又选 i
那我要留w[i]空间,所以是只能用(j-w[i]),又能再选 i ,我可以得 c[i] 所以是f[i][j-w[i]]+c[i]
不选 i
那就是f[i-1][j]
那结果是 max(f[i-1][j],f[i][j-w[i]]+c[i])
2.规律理解法
我们设 i 件选了 k 次
那要留 k * w[i] 的位置,剩 (j-k*w[i]) 价值为 k * c[i],就是f[i-1][j-k*w[i]]+k*c[i],那
f[i][j]=max{f[i-1][j],f[i-1][j-w[i]]+c[i] ,f[i-1][j-2*w[i]]+2*w[i]……,f[i-1][j-k*w[i]]+k*c[i]}
f[i][j-w[i]]=max{ f[i-1][j-w[i]] ,f[i-1][j-w[i]]+c[i] ……,f[i-1][j-k*w[i]]+(k-1)*c[i]}
后面部分就是少了个c[i],其他都一样
所以就是f[i][j]=max{f[i-1][j],f[i][j-w[i]]+c[i]}
代码:
for(int i=1;i<=n;++i)
for(int j=0;j<=m;++j){//枚举
f[i][j]=f[i-1][j];//原先是这样
if(j>w[i])//选w[i]你必须能装得下
f[i][j]=max(f[i][j],f[i][j-w[i]]+c[i]);
//求最大值
}
是否可以降维?也是可以的!
这个不需其他变化
因为这样枚举f[j-w[i]]正好是被(i)更新过了
for(int i=1;i<=n;++i)
for(int j=0;j<=m;++j)
f[j]=max(f[j],f[j-w[i]]+c[i]);