Poj 1787 Charlie's Change(多重背包 或者 记录的完全背包)
来源:互联网 发布:原语在c语言 编辑:程序博客网 时间:2024/05/16 14:52
这题首先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;}
需要解决的两个问题是:
(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;}
- -!!)
0 0
- Poj 1787 Charlie's Change(多重背包 或者 记录的完全背包)
- POJ 1787 Charlie's Change 记录路径的多重背包
- poj 1787 Charlie's Change(完全背包 或 多重背包 记录路径)
- POJ 1787 Charlie's Change(多重背包+记录路径)
- POJ-1787 Charlie's Change( 多重背包记录方案)
- poj 1787 Charlie's Change【多重背包可行性+记录路径】
- POJ 1787 - Charlie's Change(完全背包+路径记录)
- POJ 1787 Charlie's Change / 完全背包
- POJ1787:Charlie's Change(记录路径的多重背包)
- 动态规划,多重背包,保存路径,用完全背包的方法做多重背包(Charlie's Change,poj 1787)
- 【POJ】1787 Charlie's Change(完全背包)
- POJ 题目1787 Charlie's Change(完全背包)
- POJ - 1787 Charlie's Change(完全背包和路径纪录)
- poj 1787 Charlie's Change(打印路径的多重背包 ->交易数量最大化)
- POJ 1787 Charlie's Change 背包问题
- POJ 1787 Charlie's Change 背包问题
- POJ--1787--Charlie's Change--背包变形
- poj1787 Charlie's Change (多重背包+记录路径)
- matlab:求两个集合的 交集 和 并集
- 神经网络编程入门
- leetcode_195_tenth_line
- AVL树学习笔记&模板
- 事务实现原理
- Poj 1787 Charlie's Change(多重背包 或者 记录的完全背包)
- 内存管理中各属性值的区别
- Android Studio 打包、生成jks密钥、签名Apk、多渠道打包
- 补间动画
- web service系列二(几个广义的web service实现).md
- Android SharePreference框架Favor详解
- centos6.5安装Mysql5.6版本
- 神经网络训练中的训练集、验证集以及测试集合
- 二叉树已知先序序列(后序序列)、中序序列求解后序序列(先序序列)