POJ 1742 Coins
题目见此:http://poj.grids.cn/practice/1742
传说中的男人八题
解题思路:
- 第一反应当然是多重背包了,不过写了一个没有优化的直接超时,果然是男人八题之一额~
- 于是用单调队列优化,AC
- 后来上网一查,原来还可以有简单方法。这道题跟其他背包还比较不一样,是要找有几个合适的,而不是求最优解。用f数组表示总共j的钱可不可以被组合出(bool),初始化为f[0]=1,f[其余]=0;再用use_count[j]数组表示当我们用到c[i]面值的银币时,组合出j的钱需要用到的c[i]的次数(拗口~),对每一个c[i]都把它初始为0。当我们想用到c[i]面值的银币时,我们把所有可能用到c[i]的j枚举一遍,而对于每一个j(组合出的总钱数),如果它本身没有被之前的c[i]组合出来,并且j-c[i](表示我们现在要组合的总钱数减去用到的这个银币的面额)可以被之前的c[i]组合出来,并且组合它用到的c[i]次数不超过n[i],那么我们就用一次这个c[i]面值,即use_count[j] = use_count[j-c[i]] + 1。
- 还是多重背包简单直观啊,第二种方法感觉不是很好想出来嘛。
用单调队列优化的多重背包解法:
1 #include <functional> 2 #include <string.h> 3 #include <algorithm> 4 #include <stdio.h> 5 #include <cmath> 6 using namespace std; 7 const int _MAX = 100001; 8 9 struct MQ 10 { 11 int pos[_MAX]; 12 int val[_MAX]; 13 int head, tail, winlen; 14 MQ() : head(0), tail(-1), winlen(0xfffffff) {} 15 void reset(int _winlen) 16 { head = 0, tail = -1, winlen = _winlen; } 17 void push_back(int index, int _val) 18 { 19 while(head <= tail && val[tail] < _val) 20 tail--; 21 val[++tail] = _val; 22 pos[tail] = index; 23 while(abs(index - pos[head]) >= winlen) 24 head++; 25 } 26 int top() 27 { return val[head]; } 28 }; 29 30 int f[_MAX], c[101], n[101], N, V; 31 MQ q; 32 33 void Pack_mul_MQ(int cost, int weight, int num) 34 { 35 for(int d=0 ; d<cost ; d++) 36 { 37 q.reset(num+1); 38 for(int j=0 ; j<=(V-d)/cost ; j++) 39 { 40 q.push_back(j, f[j*cost+d]-j*weight); 41 f[j*cost+d] = q.top() + j*weight; 42 } 43 } 44 } 45 int main() 46 { 47 while(scanf("%d %d", &N, &V), V+N) 48 { 49 for(int i=1 ; i<=N ; i++) 50 scanf("%d", &c[i]); 51 for(int i=1 ; i<=N ; i++) 52 scanf("%d", &n[i]); 53 memset(f, 0, sizeof(f)); 54 for(int i=1 ; i<=N ; i++) 55 { 56 if(n[i] == 0) continue; 57 else if(n[i] == 1) 58 for(int j=V ; j>=c[i] ; j--) 59 f[j] = max(f[j-c[i]]+c[i], f[j]); 60 else if(V / c[i] < n[i]) 61 for(int j=c[i] ; j<=V ; j++) 62 f[j] = max(f[j-c[i]]+c[i], f[j]); 63 else 64 Pack_mul_MQ(c[i], c[i], n[i]); 65 } 66 int count = 0; 67 for(int i=1 ; i<=V ; i++) 68 if(f[i] == i) 69 count ++; 70 printf("%d\n", count); 71 } 72 return 0; 73 }
第二种解法:
1 #include <iostream> 2 #include <string.h> 3 using namespace std; 4 5 bool f[100001]; 6 int use_count[100001], c[101], n[101], N, V; 7 int main() 8 { 9 while(cin >> N >> V, V || N) 10 { 11 for(int i=1 ; i<=N ; i++) 12 cin >> c[i]; 13 for(int i=1 ; i<=N ; i++) 14 cin >> n[i]; 15 memset(f, 0, sizeof(f)); 16 f[0] = 1; 17 for(int i=1 ; i<=N ; i++) 18 { 19 memset(use_count, 0, sizeof(use_count)); 20 for(int j=c[i] ; j<=V ; j++) 21 if(!f[j] && f[j-c[i]] && use_count[j-c[i]] < n[i]) 22 { 23 f[j] = 1; 24 use_count[j] = use_count[j-c[i]] + 1; 25 } 26 } 27 int ans = 0; 28 for(int i=1 ; i<=V ; i++) 29 if(f[i]) 30 ans ++; 31 cout << ans << endl; 32 } 33 }
对于如何去理解单调队列优化的多重背包,已经在博文POJ 1276中写过
浙公网安备 33010602011771号