http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19104
本来以为对于简单的背包已经了解了,可还是在这道题上挂了彩。自己写了两种代码。
第一种是对每类钱,枚举其拿的张数 dp[i][j]=累加dp[i-1][j-k*money] 我觉得8000-的数据,N3 也无所谓的,交上去T了。
俗语云 T乃A之母 。起码说明算法是正确的,于是开始想优化的方法。
想起来 前几天做的 POJ1276 二进制优化的背包 http://blog.csdn.net/z3635363/article/details/12324849
使用这个方法把算法优化成N2logN过掉。
代码如下:
#include<stdio.h>
#include<string.h>
int dp[10000];
int money[1000];
int n;
int m[6];
int main()
{
m[0]=1;
m[1]=5;
m[2]=10;
m[3]=25;
m[4]=50;
int most;
int i,j,k;
int max=8000;
int count;
count=0;
// 二进制优化
for(i=0;i<5;i++)
{
k=1;
max=8000;
while(max-k*m[i]>=0)
{
money[count++]=k*m[i];
k=k*2;
}
}
while(scanf("%d",&most)!=EOF)
{
max=most;
count=0;
max=-1;
memset(dp,0,sizeof(dp));
dp[0]=1;
for(i=0;i<count;i++)
{
for(j=most;j-money[i]>=0;j--)
{
dp[j]+=dp[j-money[i]];
if(max<dp[j])
max=dp[j];
}
}
printf("%d\n",dp[most]);
}
}
我交上去之后就安心的等待A的返回,可是T,竟然还是T,我想不明白了,到底是怎么回事。毕竟已经优化了一步。
无奈去网上寻找答案,找到了这篇文章 http://blog.csdn.net/woshi250hua/article/details/7597909
看了他的代码 我恍然大悟,原来多重背包的dp可以这样
dp[i][j]=dp[i][j-money[k]]+dp[i-1][j];
在每一步转移的时候使用本次的计算结果来节约运算量
代码如下,交上去显示2500+ms,很危险的A了
#include <stdio.h>
#include <string.h>
#define MAX 10000
long long n,dp[MAX];
int num[5] = {1,5,10,25,50};
int main()
{
int i,j,k,tpk;
while (scanf("%lld",&n) != EOF) {
memset(dp,0,sizeof(dp));
dp[0] = 1;
for (k = 0; k < 5; ++k)
for (i = 0; i <= n; ++i)
dp[i+num[k]] += dp[i];
printf("%lld\n",dp[n]);
}
}
后记:
其实在第一次优化的时候还是浪费了很多的时间。
对无限的多重背包和有限的多重背包来说,2进制优化的方式是不同的。
对有限的多重背包,要用总量一次次减去2进制量,最后再加上剩余量,这是为了保证每一份物品都可以被利用到。
对于无线的多重背包,不必考虑物品总额度和剩余,只要2进制量的大小小于最大值即可。
具体代码参考上述代码和POJ1276 链接上部已经给出