POJ 1742 Coins

题目见此:http://poj.grids.cn/practice/1742

传说中的男人八题

解题思路:

  1. 第一反应当然是多重背包了,不过写了一个没有优化的直接超时,果然是男人八题之一额~
  2. 于是用单调队列优化,AC
  3. 后来上网一查,原来还可以有简单方法。这道题跟其他背包还比较不一样,是要找有几个合适的,而不是求最优解。用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。
  4. 还是多重背包简单直观啊,第二种方法感觉不是很好想出来嘛。

用单调队列优化的多重背包解法:

 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 }
View Code

第二种解法:

 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 }
View Code

对于如何去理解单调队列优化的多重背包,已经在博文POJ 1276中写过

posted on 2013-06-02 18:39  白~  阅读(149)  评论(0)    收藏  举报

导航