Poj 1787 Charlie's Change(多重背包 或者 记录的完全背包)

原创 2015年11月21日 16:50:01

这题首先A出的思路是多重背包,因为个数不是无限,很容易就联想到是多重背包了,

其实一开始打算用完全背包写的,因为之前做过的即到关于硬币的题目都是用完全背包写的,

这题是因为要记录个数所以就每想下去了,看了别人AC的时间,然后搜了下题解才发现可以用完全背包

题目大意:用有限个1,5,10,25这些硬币构成一个数n,要求使用硬币数最多,问每种硬币使用的个数.

思路:

多重背包:用二进制的思想,因为数 d = 2^0 + 2^1 +... + 2^k + d - 2^k; 那么就能将面值为val[i]的硬币

的个数num[i]分成k+1份因为这k+1个数能构成1->num[i]内的所有数),每种硬币都这样处理,然后01背包一遍.

因为是01背包过一遍,所以路径默认已经存储在dp内了.

多重背包AC了的代码:

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <ctime>
#include <cmath>

using namespace std;
const int N = 10010;
const int INF = 0x7fffffff;
int dp[N];
int n;
int id[4]={1,5,10,25};
int v[4],ans[4];
struct thing{
	int kk[100];
	int nk;
}thi[4];
void ZeroOnePack(int w,int vi){
	for(int i=n;i>=vi;--i){
		if(dp[i-vi]==-INF) continue;
		dp[i] = max(dp[i],dp[i-vi] + w);
//		cout<<"ZOP  dp["<<i<<"]:"<<dp[i]<<endl;
	}
}
void CompletePack(int vi){
	for(int i=vi;i<=n;++i){
		dp[i] = max(dp[i], dp[i-vi]+1);
	}
}
int main()
{
	//freopen("/home/user/桌面/in","r",stdin);
	int flag=0;
	while(scanf("%d%d%d%d%d",&n,&v[0],&v[1],&v[2],&v[3])==5){
		flag = 0;
		if(n!=0) flag=1;
		if(n==0) for(int i=0;i<4;++i)
			if(v[i]){ flag=1; break;}
		if(!flag) break;

		for(int i=0;i<4;++i){
			ans[i] = 0; thi[i].nk=0;
		}

		for(int i=1;i<=n;++i) dp[i] = -INF;
		dp[0]=0;

		for(int i=0;i<4;++i){
//			if(v[i]*id[i]>=n){
//				CompletePack(id[i]);				
//				continue;
//			}
			int m=v[i],k=1;
			while(k<m){
				ZeroOnePack(k,k*id[i]);
				thi[i].kk[thi[i].nk++] = k;
				m -= k;
				k = 2*k;
			}
			ZeroOnePack(m,m*id[i]);
			thi[i].kk[thi[i].nk++] = m;
		}
		if(dp[n]==-INF){
			printf("Charlie cannot buy coffee.\n");
			continue;
		}
		int nnn = n;
		
		for(int i=3;i>=0;--i){
			for(int j=thi[i].nk-1;j>=0;--j){
				int temp = id[i]*thi[i].kk[j];
				if(n <  temp) continue;
				if(dp[n] == dp[n - temp] + thi[i].kk[j]){
					ans[i]+=thi[i].kk[j];
					n -= temp;
				}

//				cout<<" n;"<<n<<endl;
			}
		}
		printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[0],ans[1],ans[2],ans[3]);
//		cout<<" dp["<<nnn<<"]:"<<dp[nnn]<<endl;
	}
	//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
	return 0;
}



思路2(完全背包):

需要解决的两个问题是:

(1)如何记录某面值硬币在钱为j时所使用的个数. 
(2)如何记录路径.

对于第一个问题,用一个used[N]数组来记录,转移方程是 used[j] = used[j-val[i]] + 1;

对于第二个问题,用一个path[N]数组来记录, 转移方程是 path[j]  = j - val[j];

因为是从j-val[i]推倒过来的,那么很自然路径就是由 j-val[j] -> j.


以下是完全背包AC的代码:(时间少了差不多3倍多

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <ctime>
#include <cmath>

using namespace std;
const int N = 10010;
const int INF = 0x7fffffff;
int num[4], val[4]={1, 5, 10, 25};
int dp[N], path[N], used[N], ans[100];
int main()
{
	//freopen("/home/user/桌面/in","r",stdin);
	int n;
	while(scanf("%d%d%d%d%d",&n, &num[0], &num[1], &num[2], &num[3])==5){
		if(n==0&&num[0]==0&&num[1]==0&&num[2]==0&&num[3]==0) break;

		for(int i=1;i<=n;++i) dp[i] = -INF;
		memset(path,-1,sizeof(path));

		for(int i=0;i<4; ++i){
			memset(used,0,sizeof(used));
			for(int j=val[i]; j<=n; ++j){
				if(dp[j-val[i]]!=-INF&&dp[j]<dp[j-val[i]] + 1&&used[j-val[i]]<num[i]){
					dp[j] = dp[j-val[i]] + 1;
					path[j] = j - val[i];
					used[j] = used[j-val[i]] + 1;
				}
			}
		}
//		for(int i=0;i<=n;++i) cout<<"path["<<i<<"]:"<<path[i]<<endl;
		
		if(dp[n] == -INF){
			puts("Charlie cannot buy coffee."); continue;
		}

		memset(ans,0,sizeof(ans));
		while(path[n] != -1){
			ans[n-path[n]]++;
			n = path[n];
		}
		printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]);
	}
	//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
	return 0;
}

- -!!)


POJ 1787 - Charlie's Change(完全背包+路径记录)

Description Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he o...

【POJ】1787 Charlie's Change(完全背包)

Charlie's Change

动态规划,多重背包,保存路径,用完全背包的方法做多重背包(Charlie's Change,poj 1787)

就是用拆分物品的方法做的多重背包啦,时间复杂度为O(V∑logN[i]),空间复杂度为O(V)。由于是拆分物品,所以保存路径时不但要记录转移前的位置,还要记录当前硬币的类型以及使用的个数,输出前沿着路...

POJ-1787 Charlie's Change (完全背包+输出方案 入门题)

要输出方案的完全背包 入门题。题意:10000元,4种面值硬币,给出每种硬币的个数,问最多可以用多少个硬币组合成给定的总价格,输出方案。...
  • hmc0411
  • hmc0411
  • 2017年10月30日 22:55
  • 155

poj1787 Charlie's Change 完全背包

题目链接:here 题目大意:         分硬币,有1,5,10,25四种硬币,给定每种硬币的数量,给定要组合成的价值,问刚好达到价值时用的硬币最多的情况。 分析:         感觉...

POJ1787:Charlie's Change(记录路径的多重背包)

Description Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he oft...

POJ 1787 Charlie's Change 记录路径的多重背包

Charlie's Change Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 3499   Accepted:...

POJ 1787 Change 多重|完全背包

Charlie's Change Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 3895   Accepted:...

poj1787 Charlie's Change(多重背包+路径)

http://poj.org/problem?id=1787 题意:查理想买咖啡,咖啡销售机只支持三种硬币,分别是1分、5分、10分、15分。然后给出查理拥有的四种硬币的个数,求查理最多可以消耗多少...

oj1787Charlie's Change(多重背包+记录路径,每个包恰好被填满的基础上每个包的钱币的个数尽量多)

Description Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he oft...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Poj 1787 Charlie's Change(多重背包 或者 记录的完全背包)
举报原因:
原因补充:

(最多只允许输入30个字)