hdu 3591 多重背包+完全背包练习题

1 题目

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


题意:货币系统有 N 种不同面值的钱,每种钱的价值分别为 V1,V2,...,VN 一个人要买价值和为 T 的商品,他每种分别相应的带了 C1,C2,...,CN ,然后问你交易完成后所需要经手的钱币最少数目
思路:先对人进行多重背包,然后对售货员进行完全背包
答案其实和以前做的那题 背包超过容量的处理是一样的(uva 10819)
一直情况就是那个人所带的钱刚好凑成了 T ,那么就不需要找钱了,否则就需要找钱,相当于那个人支付了他所能付的超过 T 的钱 M,然后售货员找钱 M-T ,把这两者的经手的钱数加起来就行了
初始化的时候除了 dp[0]=0 之外,其它的都为 inf 表示 i 这个状态是可以由商品组合起来的
否则如果全部初始化为 0 的话,i 只能说是一个上界

1.2 题解

But Xiaoqian is a low-pitched girl , she wouldn’t like giving out more than 20000 once.一次不会付钱超过20000块。
背包容量是钱的价值,重量是钱的数目(每个钱币的重量都是1),求给出相同的钱的价值可以使用的最少(注意不是最大)的钱币数量。
动态规划求的是最小值。

对于主人公方用多重背包求,售后员方用完全背包求。 然后背包容量之差恰好等于T的情况中取使用硬币数目最少的。

      ans = INT_MAX;
        for(i=T;i<20001;i++)
            ans = min(ans,dp[i]+dp[i-T])

2 AC代码

2,.1 版本一(超时了)

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


int inf = 20001; //足够大的数

int v = 20000; //背包的容量

//处理一件01背包中的物品
void ZeroOnePack(int dp[], int cost, int weight)
{
	for (int i = v; i >= cost; i--)//descend
		dp[i] = min(dp[i], dp[i - cost] + weight);
}

//deal with one item
void CompletePack(int dp[], int cost, int weight)
{
	for (int i = cost; i <= v; i++)
		dp[i] = min(dp[i], dp[i - cost] + weight);
}

//deal with one item
void MultiplePack(int dp[], int cost, int weight, int number)
{
	if (cost*number >= v) //注意等于号
	{
		//物品足够多转化为完全背包
		CompletePack(dp, cost, weight);
		return;
	}
	int k = 1;
	while (k < number)//二进制拆分
	{
		ZeroOnePack(dp, k*cost, k*weight);
		number -= k;
		k << 2;
	}
	ZeroOnePack(dp, cost*number, weight*number);
}


int main(int)
{
	int dp[20002], dp2[20002];//背包容量是钱的价值,重量是钱的数目(每个钱币的重量都是1),求给出相同的钱的价值可以使用的最少(注意不是最大)的钱币数量
	//But Xiaoqian is a low-pitched girl , she wouldn’t like giving out more than 20000 once.一次不会付钱超过20000块
	int N, T, v[101], c[101], cnt = 0, i, j, ans;
	while (scanf("%d %d", &N, &T) != EOF && (N != 0 || T != 0))
	{
		cnt++;
		if (N == 0 || T == 0)
		{
			printf("Case %d: %d\n", cnt, 0);
			continue; //这里打成continue会卡在那里一直等待输入,报time limit exceeded
		}
		for (i = 1; i <= N; i++)
			scanf("%d", &v[i]);
		for (i = 1; i <= N; i++)
			scanf("%d", &c[i]);
		

		//初始化
		for (i = 0; i<20002; i++)
			dp[i] = inf, dp2[i] = inf;  //这里的初值只能设置为足够大,而不能是INT_MAX,否则dp[i - cost] + weight会由于溢出变成负数!!!
		dp[0] = dp2[0] = 0; //注意dp[0]是有意义的,要单独初始化!!!
		//背包
		for (i = 1; i<=N; i++) //这里的上界应该是物品的数量
			CompletePack(dp2, v[i], 1);

		for (i = 1; i<=N; i++)//这里的上界应该是物品的数量
			MultiplePack(dp, v[i], 1, c[i]);



		ans = INT_MAX;
		int t = inf;
		for (i = T; i<20001; i++)
		if (ans > dp[i] + dp2[i - T])
		{
			ans = dp[i] + dp2[i - T];
			t = i;
		}


		!(dp[t] == inf || dp[t - T] == inf) ? printf("Case %d: %d\n", cnt, ans) : printf("-1\n");

	}

	return 0;
}
/**
3 70
5 25 50
5 2 1
0 0

**/


158936032015-12-18 14:19:58Time Limit Exceeded35911000MS1664K2186 BG++aaaaaaaaaaaaalc

说实话不明白这个代码为什么会超时,这个代码跟下面一个版本本质上是一样的。



2.2 版本二

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e2 + 10;//110
const int maxm = 2e4 + 10; //20010 
const int INF = 1e8;
int c[maxn], v[maxn], f[maxm], g[maxm];
int main()
{
	int n, m, tt = 0;
	while (scanf("%d%d", &n, &m) != EOF)
	{
		if (n == 0 && m == 0)break;
		int i, j, k;
		for (i = 0; i<n; i++)
			scanf("%d", &v[i]);
		for (i = 0; i<n; i++)
			scanf("%d", &c[i]);
		for (i = 1; i <= 20000; i++)
		{
			g[i] = f[i] = INF;
		}
		f[0] = g[0] = 0;
		//完全背包
		for (i = 0; i<n; i++)
		{
			for (j = v[i]; j <= 20000; j++)
				g[j] = min(g[j], g[j - v[i]] + 1);
		}
		//多重背包
		for (i = 0; i<n; i++)
		{
			if (v[i] * c[i] >= 20000)
			{
				for (j = v[i]; j <= 20000; j++)
					f[j] = min(f[j], f[j - v[i]] + 1);
				continue;
			}
			for (k = 1; k<c[i]; k = k * 2)
			{
				for (j = 20000; j >= v[i] * k; j--)
					f[j] = min(f[j], f[j - v[i] * k] + k);
				c[i] -= k;
			}
			for (j = 20000; j >= c[i] * v[i]; j--)
				f[j] = min(f[j], f[j - v[i] * c[i]] + c[i]);
		}

		int ans = INF;
		for (i = m; i <= 20000; i++)
			ans = min(ans, f[i] + g[i - m]);
		if (ans == INF)ans = -1;
		printf("Case %d: %d\n", ++tt, ans);
	}
	return 0;
}
158930972015-12-18 13:14:08Accepted359146MS1716K1344 BG++aaaaaaaaaaaaalc


3 总结

     目前对dp[]数组初始化对程序的影响过程不算很清楚。

3.2  初始化问题

注意dp[0]是有意义的,要单独初始化!!!

dp[i] = inf, dp2[i] = inf;  //这里的初值只能设置为足够大,而不能是INT_MAX,否则dp[i - cost] + weight会由于溢出变成负数!!!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值