前情提要动态规划(完全背包问题)-CSDN博客https://mp.csdn.net/mp_blog/creation/editor/123909609
你已经学会了完全背包问题的写法,那么让我们看看是否能优化这个代码吧;
通过观察我们代码,比起01问题,我们新加入的代码有一个比较过程,那么基于此,我们来找规律吧;
g[i][j]=max(g[i][j],g[i][j-v[i]]+w[i],g[i][j-2*v[i]]+2*w[i]...)
g[i][j-v[i]]=max( g[i][j-v[i]], g[i][j-2*v[i]]+w[i] ...)
g[i][j-2*v[i]]=max( g[i][j-2*v[i]] ...)
我们在找max的时候,在条件(j>=v[i])成立的时候,是从多个值中找一个最大的,这时我们发现:当j>=2*v[i]时成立,每一个式子除了第一项,他的其余值成为一个集合时,等于他的下一个式子内,可以取最大值的集合+w[i];
这个时候我们就有了一个等价关系:
g[i][j]=max(g[i][j],g[i][j-v[i]]+w[i]);
这段代码是一定成立的,因为g[i][j-v[i]]一定是在g[i][j]之前被计算出来的,所以我们可以直接拿来比较,于是我们的代码就得到了如下简化:
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int v[N],w[N];
int g[N][N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];//正常输入我们物品的价值
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
g[i][j]=g[i-1][j];//正常继承,如果不正常继承,可能会导致在j<v[i]时
//这时下面不等式成立,于是这里没有变化还是0,
//但其实可能会有其他优解而导致答案错误
if(j>=v[i])
g[i][j]=max(g[i][j],g[i][j-v[i]]+w[i]);
}
cout<<g[n][m];
}
至此...没有结束!!!
我们发现,这里的代码和01背包问题很相似,不妨大胆一点我们来直接优化乘一维,方法与之前的一样。首先,我们需要去掉一维,然后对下面进行改动,如果改变合理就保留,否则就修改;
那么我们可以得到如下代码:
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int v[N],w[N];
int g[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];//正常输入我们物品的价值
for(int i=1;i<=n;i++)
for(int j=v[i];j<=m;j++)
g[j]=max(g[j],g[j-v[i]]+w[i]);
//g[i][j]=max(g[i][j],g[i][j-v[i]]+w[i]);
cout<<g[m];
}
这个时候小伙伴问了,诶呀,你刚才改的时候都把j的遍历顺序改变了,这次怎么不改呢?
原因是,我们发现,01背包问题考虑的是是否带第i个物品,这就导致了我们需要取第i-1行的数据来计算最优解,但是这里我们没有用到底i-1行,这也是我们可以直接删除一维的原因。
至此,完结撒花;’