博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
poj 1787
阅读量:4686 次
发布时间:2019-06-09

本文共 3393 字,大约阅读时间需要 11 分钟。

地址:

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

mark:多重背包!本题给出两种方法,特别注意下面一个方法!!!

代码:

需要回溯,于是就加一个path[]存放父亲,dp[]代表个数。由于该题有5,10这两个硬币,多重背包二进制优化的时候可能出现5*2,然后本题要区分开来,所以记录一下就行。

#include 
#include
#include
const int N = 10010;int vmax, v[5] = {
1, 5, 10, 25};int num[30], dp[N], path[N];int max(int a, int b) {
return a > b ? a : b;}void zero(int p, int k) //01背包 { for(int j = vmax; j >= k*v[p]; j--) { if(dp[j-k*v[p]]+k > dp[j]) { dp[j] = dp[j-k*v[p]]+k; path[j] = p*N+(j-k*v[p]); } }}void com(int p) //完全背包 { for(int j = v[p]; j <= vmax; j++) { if(dp[j-v[p]]+1 > dp[j]) { dp[j] = dp[j-v[p]]+1; path[j] = p*N+(j-v[p]); } }}void mut() //多重背包 { for(int p = 0; p < 4; p++) { if(num[p] == 0) continue; if(v[p] > vmax) return ; int amount = num[p]; if(v[p]*amount >= vmax) com(p); else { for(int j = 1; j <= amount; j*= 2) { zero(p, j); amount -= j; } if(amount) zero(p, amount); } }}int main(){ while(scanf("%d", &vmax)) { for(int j = 0; j < 4; j++) scanf("%d", num+j); if(!vmax) break; for(int k = 1; k <= vmax; k++) dp[k] = -N; dp[0] = 0; path[0] = -1; mut(); if(dp[vmax] <= 0) { puts("Charlie cannot buy coffee."); continue; } memset(num, 0, sizeof(num)); while(vmax) { int s1 = path[vmax]/N; int s2 = path[vmax]%N; num[v[s1]] += (vmax-s2)/v[s1]; vmax = s2; } printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", num[1], num[5], num[10], num[25]); } return 0;}

学习大牛的思路,本题还可以应用完全背包的思路,只不过要多加一个数组used[]存放已经用了多少个同种类的硬币,不能超过给定数量。

注意:这一类题目可以按照一下分析来解决问题,效率是O(NV)的,比二进制优化快!

因为本题是要求硬币尽可能多,所以在更新节点的时候同样的价值大的硬币肯定能不用则不用,只要用了肯定是当前最优的情况,所以可以用used数据记录用过的硬币。但是如果一旦题目变成了硬币尽可能少,那么这种方法就错了,因为价值相同的时候 ,肯定大硬币用的多,数量就少,这就会导致一个问题,就是往后扫描的时候,可能出现大硬币已经用完了,本来这个价值是可以达到的,但是因为这个方法而导致这个价值达不到。那转换个方向,我们可以从硬币最大的开始更新嘛~

#include 
#include
#include
const int N = 100010;int v[5] = {
1, 5, 10, 25};int p,num[30];int dp[N],used[N],path[N];int main(){ int i,j,k; while(scanf("%d", &p)) { for(i = 0; i < 4; i++) scanf("%d", num+i); if(!p) break; memset(dp, -1, sizeof(dp)); dp[0] = 0; for(i = 0; i < 4; i++) { memset(used, 0, sizeof(used)); for(j = v[i]; j <= p; j++) { if(dp[j-v[i]]+1 > dp[j] && dp[j-v[i]] != -1 && used[j-v[i]] < num[i]) { dp[j] = dp[j-v[i]]+1; used[j] = used[j-v[i]]+1; path[j] = j-v[i]; } } } if(dp[p] == -1) {puts("Charlie cannot buy coffee."); continue;} memset(num, 0, sizeof(num)); while(dp[p]) { 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]); } return 0;}

转载于:https://www.cnblogs.com/andre0506/archive/2012/09/22/2697788.html

你可能感兴趣的文章
fatal: remote origin already exists.
查看>>
gridview 自定义value值
查看>>
2018二月实现计划成果及其三月规划
查看>>
封装springmvc处理ajax请求结果
查看>>
tyvj P2018 「Nescafé26」小猫爬山 解题报告
查看>>
类名.class和getClass()区别
查看>>
开发脚本自动部署及监控
查看>>
JavaScript--语句
查看>>
12/17面试题
查看>>
css 继承和层叠
查看>>
javascript实现图片轮播3D效果
查看>>
ssl初一组周六模拟赛【2018.3.17】
查看>>
[RxJS] Avoid mulit post requests by using shareReplay()
查看>>
C++和C#之间的数据类型对应关系
查看>>
模型分离(选做)
查看>>
LeetCode 242. Valid Anagram
查看>>
观察者模式------《Head First 设计模式》
查看>>
JSP表单提交乱码
查看>>
如何适应现代雇佣关系
查看>>
【BZOJ4592】[Shoi2015]脑洞治疗仪 线段树
查看>>