POJ 1787 Charlie‘s Change 背包系列 + 路程记录
乍一看。多重背包。
再乍。转化01背包?nonono,每样物品的数量达到了10^4,肯定会T。
再乍。看不出来了。去看大牛的题解吧。
哦——转化成完全背包。
怎么转?一般的完全背包是不限个数的。现在我们每一次枚举物品时记录一下取了多少个然后限制一下不要超过个数就好了。
恩。。
路程记录的话完全背包加一个path记录父亲。。然后多重背包加一个num数组在dp的时候直接算一算。。具体看代码。。完全背包的那版路径记录感觉十分巧妙呀嘿嘿嘿
转换方程还是老样子。。。。。。。。
f[i][j]表示用前i种硬币得到价值k时的最优解
当且仅当上一个状态可达的时候( 不为0 )才转移。
状态方程为:
f[i][j] = f[i - 1][j - val[i]] + 1; ( f[i - 1][j - val[i]] > 0 && f[i - 1][j] < f[i][j - val[i]] + 1 )
//完全背包还要加一个num[j - val[i]] + 1 <= limit[i],num为当前使用第i种的个数,limit时第i种可用的数量。
然后我们滚动数组滚一下优化一下空间
。。为什么这个做法是对的呢。。。
我们从小硬币开始遍历。。
如果是求最少的方案的话。。。
从大的硬币开始遍历就好了吧。。。【以上都是我口胡的
渣代码:
1 #include <iostream> 2 #include <string.h> 3 #include <math.h> 4 #include <algorithm> 5 #include <stdlib.h> 6 #include <stdio.h> 7 #define ll long long 8 using namespace std; 9 const int N = 10010; 10 int f[N], n, path[N], c[5], val[5] = { 0, 1, 5, 10, 25 }, ans[N][5]; 11 void zeroone(int val, int num, int pos) 12 { 13 for(int i = n; i >= val; i --){ 14 if(f[i - val] && f[i - val] + num > f[i]){ 15 f[i] = f[i - val] + num; 16 for(int j = 1; j <= 4; j ++){ 17 ans[i][j] = ans[i - val][j]; 18 } 19 ans[i][pos] += num; 20 } 21 } 22 } 23 void complete(int val, int pos) 24 { 25 for(int i = val; i <= n; i ++){ 26 if(f[i - val] && f[i - val] + 1 > f[i]){ 27 f[i] = f[i - val] + 1; 28 for(int j = 1; j <= 4; j ++){ 29 ans[i][j] = ans[i - val][j]; 30 } 31 ans[i][pos] += 1; 32 } 33 } 34 } 35 void multi(int num, int val, int pos) 36 { 37 if(num * val >= n){ 38 complete(val, pos); return ; 39 } 40 int tmp = 1; 41 while(num >= tmp){ 42 zeroone(tmp * val, tmp, pos); 43 num -= tmp; 44 tmp <<= 1; 45 } 46 if(num) zeroone(num * val, num, pos); 47 } 48 int main() 49 { 50 while(~scanf("%d%d%d%d%d", &n, &c[1], &c[2], &c[3], &c[4]) && n + c[1] + c[2] + c[3] + c[4]){ 51 memset(ans, 0, sizeof(ans)); 52 memset(path, 0, sizeof(path)); 53 memset(f, 0, sizeof(f)); 54 f[0] = 1; 55 for(int i = 1; i <= 4; i ++){ 56 multi(c[i], val[i], i); 57 } 58 if(!f[n]){ 59 printf("Charlie cannot buy coffee.\n"); continue; 60 } 61 printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[n][1],ans[n][2],ans[n][3],ans[n][4]); 62 } 63 return 0; 64 }
多重背包
1 #include <iostream> 2 #include <string.h> 3 #include <math.h> 4 #include <algorithm> 5 #include <stdlib.h> 6 #include <stdio.h> 7 #define ll long long 8 using namespace std; 9 const int N = 10010; 10 int f[N], n, path[N], c[5], val[5] = { 0, 1, 5, 10, 25 }, ans[30], num[N]; 11 int main() 12 { 13 while(~scanf("%d%d%d%d%d", &n, &c[1], &c[2], &c[3], &c[4]) && n + c[1] + c[2] + c[3] + c[4]){ 14 memset(ans, 0, sizeof(ans)); 15 memset(path, 0, sizeof(path)); 16 memset(f, 0, sizeof(f)); 17 f[0] = 1; 18 for(int i = 1; i <= 4; i ++){ 19 memset(num, 0, sizeof(num)); 20 for(int j = val[i]; j <= n; j ++){ 21 if(f[j - val[i]] && f[j - val[i]] + 1 > f[j] && num[j - val[i]] + 1 <= c[i]){ 22 f[j] = f[j - val[i]] + 1; 23 num[j] = num[j - val[i]] + 1; 24 path[j] = j - val[i]; 25 } 26 } 27 } 28 if(!f[n]){ 29 printf("Charlie cannot buy coffee.\n"); continue; 30 } 31 while(n > 0){ 32 ans[n - path[n]] ++; 33 n = path[n]; 34 } 35 printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]); 36 } 37 return 0; 38 }
完全背包
下面是两个方法最后跑出来的时间对比。上面的是完全背包,下面的是多重背包。
DONE.
------------------------------
POJ 1787 Charlie's Change
时间: 2024-10-13 10:17:56