您的位置:首页 > 其它

dp之01背包(从二维到一维)+完全背包

2017-08-14 17:04 363 查看


☝这是最原始的01背包问题http://101.200.220.237/problem/153/

下面写一下我的想法。

巨巨是这样说的:

能用动规解决的问题的特点

1) 问题具有最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质

2)无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。

解释一下状态转移方程f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);

就是第i个物体放入容量为j的背包时所能获得的最大价值,划分了子问题,就是本来背包是m那么大,然后分割成1,2,3…m,那么大,符合第一条,无后效性在自己打表的时候体会比较深刻一些。

继续说状态转移方程,这样的话对于f[i][j],就只存在放或者不放第i个物体这两种选择,前提是能放进去。如果不放,那自然就是直接取f[i-1][j]的值;如果放,这就牵扯到一个“腾地方”的问题,就是你想放进去一个多大的东西,那么就需要给它腾出多大的地方,然后比如腾完了地方如果还有位置还是可以放别的东西的,而这个数据在上一层已经记录,所以就有了f[i-1][j-weight[i]]+value[i]。

样例中的数据打表得出的结果是:



自己把这个表写一遍很重要,写完差不多就懂了。

基本上算是最基本的dp代码了吧。

#include<iostream>
using namespace std;
#include<algorithm>
#include<string>
#include<string.h>

int main()
{
int n, m;
while (scanf("%d%d", &n, &m) != EOF)
{
int f[110][1100];//表格记录数值
memset(f, 0, sizeof(f));
int weight[110];
int value[110];
int i, j;
for (i = 1; i <= n; i++)
scanf("%d%d", &weight[i], &value[i]);
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
{
if (weight[i] <= j)
f[i][j] = max(f[i - 1][j], f[i - 1][j - weight[i]] + value[i]);
//状态转移方程
else

4000
f[i][j] = f[i - 1][j];
}
printf("%d\n", f
[m]);
}
return 0;
}


今天还学习了一个把二维表简化成一维表的。

据说这个对后续的“背包九讲”的学习有很大的帮助。

开始我以为只是简单地将二维缩成一维,但是当然就不是!

#include<iostream>
using namespace std;
#include<string.h>
#include<algorithm>

int main()
{
int t;
cin >> t;
while (t--)
{
int weight[1010];
int value[1010];
int f[10010];
memset(f, 0, sizeof(f));
int n, w;
cin >> n >> w;
int i,j;
for (i = 1; i <= n; i++)
cin >> value[i];
for (i = 1; i <= n; i++)
cin >> weight[i];
for (i = 1; i <= n; i++)
for (j = w; j >=0; j--)//为什么能变成一维
if (weight[i]<=j)
f[j] = max(f[j], f[j - weight[i]] + value[i]);

cout << f[w] << endl;
}
return 0;
}


跑出来的结果是这样的。(每一个i循环中都把f数组从1到w输出一下)



这个是按照j正序跑出来的结果。



这是为什么呢?就按程序中的跑,正序的时候的一个输出的f数组,那么f[10]=max(f[10],f[5]+value[5]),也就是说这个第一个东西被放进去了两次!如果是从w…0,就不会出现这种情况,这个真的好机智,然后就可以直接算完全背包了!

hdu4508,可是说是完全背包的裸题了

http://acm.hdu.edu.cn/showproblem.php?pid=4508

#include<iostream>
using namespace std;
#include<algorithm>
#include<string.h>

int happiness[110];
int cla[110];
int dp[100006];

int main()
{
int n;
while (scanf("%d",&n)!=EOF)
{
memset(dp, 0, sizeof(dp));
int i;
for (i = 0; i < n; i++)
scanf("%d%d", &happiness[i], &cla[i]);
int m;
scanf("%d", &m);
int j;
for (i = 0; i < n; i++)
for (j = cla[i]; j <= m; j++)//仔细感受一下这个循环,可以说是非常机智了
dp[j] = max(dp[j], dp[j - cla[i]] + happiness[i]);

printf("%d\n", dp[m]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: