Poj 1787 Charlie's Change (DP_背包)

题目链接:http://poj.org/problem?id=1787


题目大意:给定一个数p,要求用四种币值为1,5,10,25的硬币拼成p,并且硬币数要最多,如果无解输出"Charlie cannot buy coffee.",1<=p<=1万,1<=硬币数量<=1万


解题思路:看到题目的第一想法是多重背包,把币值和p看成容量,硬币数看成价值,问题就转换为最容量为p的背包中价值最大为多少?刚入手用二进制处理将多重背包转换为01背包然后求解,速度奇慢,跑了969ms,优化后变成675ms。复杂度度为O(p*sum(log(num[i]))。AC之后看别人的解题报告,惊讶地发现他们都用完全背包写这题,复杂仅为(4*p),速度快了5倍多。

    两种背包的状态转移方程都一样:dp[j] = max(dp[j],dp[j-mon[i]+val[i])(多重背包解法val[i]>=1,1<=i<=tot,完全背包解法val[i]=1,1<=i<=4)


测试数据:

12 5 3 1 2

16 16 0 0 1

1 0 0 0 0


代码:

//完全背包
#include <stdio.h>
#include <string.h>
#define MAX 20000


int p,dp[MAX],path[MAX];
int num[50],mon[50],used[MAX];


int main()
{
	int i,j,k,tp;
	mon[1] = 1,mon[2] = 5;
	mon[3] = 10,mon[4] = 25;


	while (scanf("%d",&p)) {

		for (i = 1; i <= 4; ++i)
			scanf("%d",&num[i]);
		if (p == 0) break;
		memset(dp,0,sizeof(dp));
		memset(path,0,sizeof(path));
		path[0] = -1,dp[0] = 1;


		for (i = 1; i <= 4; ++i) {

			memset(used,0,sizeof(used));
			for (j = mon[i]; j <= p; ++j)
				if (dp[j-mon[i]] + 1 > dp[j] && dp[j-mon[i]]
					&& used[j-mon[i]] + 1 <= num[i]) {

					dp[j]   = dp[j-mon[i]] + 1;
					used[j] = used[j-mon[i]] + 1;
					path[j] = j - mon[i];
				}
		}



		if (dp[p] != 0) {
			
			memset(num,0,sizeof(num));
			while (path[p] != -1) {

				num[p-path[p]]++;
				p = path[p];
			}
			printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",num[1],num[5],num[10],num[25]);
		}
		else printf("Charlie cannot buy coffee.\n");  
	}
	return 0;
}
//多重背包
#include <stdio.h>
#include <string.h>
#define MAX 20000
#define INF 1000000000
#define max(a,b) (a)>(b)?(a):(b)


int num[5],mon[5],in[MAX];
int tot,cost[MAX],val[MAX];
int p,ans,dp[100][MAX],path[100][MAX];


void Solve_1A() {

	int i,j,k;
	tot = 0;
	for (i = 1; i <= 4; ++i) {

		for (j = 0; (1<<j) <= num[i]; ++j) {

			k = 1<<j;
			num[i] -= k;
			cost[++tot] = k * mon[i];
			val[tot]  = k;
			in[tot] = i;
		}
		if (num[i])  {

			in[++tot] = i;
			val[tot]  = num[i];
			cost[tot] = num[i] * mon[i];
		}
		num[i] = 0;				//最后拿来存放答案,老师说要懂得回收利用
	}


	for (i = 0; i <= tot; ++i)
		for (j = 0; j <= p; ++j)
			path[i][j] = 0,dp[i][j] = -INF;


	dp[0][0] = 0;
	for (i = 1; i <= tot; ++i) {

		for (j = p; j >= 0; --j)
			if (j >= cost[i]) {

				if (dp[i-1][j-cost[i]] != -INF 
					&& dp[i-1][j-cost[i]] + val[i] > dp[i-1][j])
					dp[i][j] = dp[i-1][j-cost[i]] + val[i],path[i][j] = 1;
				else dp[i][j] = dp[i-1][j];
			}
			else dp[i][j] = dp[i-1][j];
	}


	if (dp[tot][p] != -INF) {

		j = tot,k = p;
		while (j > 0) {

			if (path[j][k]) 
				num[in[j]] += val[j],k -= cost[j];
			j--;
		}
	}
}


int main()
{
	int i,j,k;
	mon[1] = 1,mon[2] = 5;
	mon[3] = 10,mon[4] = 25;


	while (scanf("%d",&p)) {

		for (i = 1; i <= 4; ++i)
			scanf("%d",&num[i]);
		if (p == 0) break;


	
		Solve_1A();
		if (dp[tot][p] == -INF)
			printf("Charlie cannot buy coffee.\n");
		else 
			printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",num[1],num[2],num[3],num[4]);
	}
	return 0;
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(优化,测试,Path)