早就听闻动态规划是一个十分神奇的东西,也仅仅是听闻这一个东西。
有决心学习动态规划,但拖到了现在。
为什么不继续拖了呢?
(自己逼的)
两周后要给21级的学弟学妹讲授“动态规划背包问题”这个部分,就是因为动归如此重要而且我还不会,才选择的这个篇目讲授,反向思考?倒逼输入了。
再想想两周后期末考试复习周,英语四级等其他事情。发现时间更加有限了。
打开了信息学奥赛一本通的刷题网站,第一个动归背包问题1267:01背包。
边做边调那么半小时,我的心情是黑色的。。。
错误代码:
#include<bits/stdc++.h>
using namespace std;
int f[100][1000];
int w[100],c[100];
int main()
{
int m;
int n;
cin >> m >> n;
for(int i=1;i<=n;i++)
{
cin >> w[i] >> c[i];
}
for(int i=1;i<=m;i++)
for(int j=n;j>=1;j--)
{
if(i>=w[j])
f[j][i] = max(f[j-1][i],f[j-1][i-w[j]]+c[j]);
else
f[j][i] = f[j-1][i];
}
cout << f[n][m];
}
这个水平还讲课??????
这不得重视一下子。
正确代码是这个样子的:
(有那么点误差)。。。
#include<cstdio>
#include<iostream>
using namespace std;
const int maxm = 201,maxn = 31;
int m,n;
int w[maxn],c[maxn];
int f[maxn][maxm];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(int i=1;i<=n;i++)
for(int v=m;v>0;v--)
if(w[i]<=v)
f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);
else
f[i][v]=f[i-1][v];
printf("%d",f[n][m]);
return 0;
}
分析一下哪里的问题:
错误的地方:
for(int i=1;i<=m;i++)
for(int j=n;j>=1;j--)
{
if(i>=w[j])
f[j][i] = max(f[j-1][i],f[j-1][i-w[j]]+c[j]);
else
f[j][i] = f[j-1][i];
}
正确的做法:
for(int i=1;i<=n;i++)
for(int v=m;v>0;v--)
if(w[i]<=v)
f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);
else
f[i][v]=f[i-1][v];
01背包的两重循环:
第一个for循环物品数,第二个for倒着循环重量。
dp的二维数组,第一个数是物品数,第二个数是重量。
(第一次写的没有if,第二次没有else,第三次dp循环错了)
就这水平,还。。害,继续看一看。
下一个题1268:完全背包:
(有那么点印象,把第二层循环反过来,就好了!(可结果不是我想的那样的))
#include<bits/stdc++.h>
using namespace std;
int f[100][1000];
int w[100],c[100];
int main()
{
int m;
int 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=1;j<=m;j++)
{
if(j>=w[i])
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
else
f[i][j] = f[i-1][j];
}
cout <<"max="<< f[n][m];
}
很显然,Wrong Answer了。
不自己想了,看这个答案吧:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxm=201,maxn=31;
int m,n;
int w[maxn],c[maxn];
int f[maxn][maxm];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(int i=1;i<=n;i++)
for(int v=1;v<=m;v++)
if(v<w[i])
f[i][v]=f[i-1][v];
else
if(f[i-1][v]>f[i][v-w[i]]+c[i])
f[i][v]=f[i-1][v];
else
f[i][v]=f[i][v-w[i]]+c[i];
printf("max=%d", f[n][m]);
return 0;
}
不仅是第二层循环反过来,还是小瞧了完全背包。
这不得再好好分析一下:
重要部位:
俺的:
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
对的:
f[i][v]=max(f[i-1][v],f[i][v-w[i]]+c[i]);
??完全背包哇,物品无限多,所以呢。
第二项f[i][j-w[i]]+c[i],中的f[i]不能-1了。
行,对01背包和完全背包的内容有点熟悉了。
但一想到要讲授给小朋友们,还差的多还差的多(冒汗)。
再深入的理解01背包,(主要是俺想知道为啥二重循环,为啥正着倒着,为啥dp)
首先,动归原理:
大问题拆成小问题,解决小问题,递推大问题。
递推关系式:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+c[i]);
第i,j个状态,取上一个状态的最大值。
即, 上一个状态有两种情况,不变dp[i-1][j],
或者背包容量减,价值加,dp[i-1][j-w[i]]+c[i]。
所以,为啥循环:一循环物品数,二循环背包容量。
为啥逆序:01背包的dp[i][]只和dp[i-1][]有关。
同时完全背包正序:考虑第i种物品出现的问题,会对下一个情况造成影响。
01背包和完全背包先这样。