1. 地址
http://acm.hdu.edu.cn/showproblem.php?pid=2602
2. 定位
动态规划
0-1背包
3. 分析
3.1 典型0-1背包
状态转移方程为 dp[i,v]=max(dp[i−1,v],dp[i−1,v−volume[i]]+value[i]) 。
dp[i,v]
表示前
i
件物品经选择后放入容量为
不放入第 i 件物品,即
dp[i,v]=dp[i−1,v] 放入第 i 件物品,同时为其加入腾出足够的空间容量,即
dp[i,v]=dp[i−1,v−volume[i]]+value[i]
放入第
i
件物品可能意味着在前
3.2 存储空间优化
分析状态转移方程, dp[i,v] 仅与 dp[i−1,v] 和 dp[i−1,v−volume[i]] 有关,采用二维数组完整地存储 dp[1][],dp[2][],...,dp[i−1][] 是一种存储空间上的浪费,我们需要的只是 dp[i−1][] 。另外,由于 dp[i−1][x]x<=v ,采用数组逆序更新的方式可以保证上一状态的先使用后更新。
因此,将问题的存储空间由二维数组优化为一维数组,状态转移方程为 dp[v]=max(dp[v],dp[v−volume[i]]+value[i]) 。
3.3 边界处理
dp[0] 看似荒谬,实则是有意义的:
v−volume[i] 很容易出现非正数的情况: v−volume[i]<0 说明背包无法容纳第 i 件物品,舍去;
v−volume[i]=0 相当于在背包中装入首个物品。dp[0] 表示背包容量为0时背包的总价格,它不一定为0。若某物品体积为0且价格不为0,则背包容量为0时其价格也为正数,这是本题陷阱之一。
不论题中有无此陷阱,建议保留 dp[0] ,尽量避免特殊情况的处理,保证算法逻辑的一般性。
4. 代码
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 1001
int volume[MAX_N];
int value[MAX_N];
int dp[MAX_N];
int main()
{
int T,N,V;
int i,j;
scanf("%d*c",&T);
while(T--)
{
scanf("%d*c",&N);
scanf("%d*c",&V);
memset(volume,0,sizeof(volume));
memset(value,0,sizeof(value));
memset(dp,0,sizeof(dp));
for(i=1; i<=N; i++)
{
scanf("%d*c",&value[i]);
}
for(i=1; i<=N; i++)
{
scanf("%d*c",&volume[i]);
}
for(i=1; i<=N; i++)
{
for(j=V; j>=volume[i]; j--)
{
dp[j] = dp[j-volume[i]] + value[i] > dp[j] ? dp[j-volume[i]] + value[i] : dp[j];
}
}
printf("%d\n",dp[V]);
}
return 0;
}
5. 性能
Exe.Time | Exe.Memory | Code Length | Language |
---|---|---|---|
31MS | 1516K | 840B | c |
6. 未解决问题
6.1 无符号整型的处理效率
在本题中,将数组声明为无符号整型,运算超时;声明为有符号整型后,则顺利通过。对此,提出以下几点猜想:
无符号数运算耗时
memset()
对无符号数的操作耗时
Ver 1.0 2017-9-18