UVA 674 背包之殇

17 篇文章 0 订阅

题目地址 :

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 链接上部已经给出


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值