PKU 1276 Cash Machine

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

解题思路:

  1. 一看就是多重背包,没什么说的了……
  2. 一开始不会单调队列优化,后来写了个优化了,可以对照着看看,加深理解

未用单调队列优化:

 1 #include <iostream>
 2 #include <string.h>
 3 using namespace std;
 4 #define sum(a, b) a+b
 5 const int MAXN = 11, MAXV = 100010;
 6 int f[MAXV], c[MAXN], w[MAXN], n[MAXN], N, V;
 7 
 8 void Pack_01(int cost, int weigh)
 9 {
10     for(int j=V ; j>=cost ; j--)
11         f[j] = max(f[j], f[j-cost]+weigh);
12 }
13 void Pack_Com(int cost, int weigh)
14 {
15     for(int j=cost ; j<=V ; j++)
16         f[j] = max(f[j], f[j-cost]+weigh);
17 }
18 void Pack_Mul(int cost, int weigh, int amount)
19 {
20     if(cost * amount >= V)
21     {    Pack_Com(cost, weigh);    return;}
22     for(int k=1 ; k<amount ; k=k<<1)
23     {
24         Pack_01(k*cost, k*weigh);
25         amount -= k;
26     }
27     Pack_01(amount*cost, amount*weigh);
28 }
29 
30 int main()
31 {
32     while(cin >> V)
33     {
34         memset(f, 0, sizeof(f));
35         cin >> N;
36         for(int i=1 ; i<=N ; i++)
37             cin >> n[i] >> c[i];
38         for(int i=1 ; i<=N ; i++)
39             Pack_Mul(c[i], c[i], n[i]);
40         cout << f[V] << endl;
41     }
42 }
View Code

 

单调队列优化:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 using namespace std;
 5 
 6 const int MAXV = 100001, MAXN = 1001;
 7 int N, V, f[MAXV], c[MAXN], n[MAXN];
 8 
 9 struct MQ
10 {
11     int pos[MAXV], val[MAXV];
12     int head, tail, winlen;
13     void reset(const int& _winlen)
14     {    head = 0, tail = -1, winlen = _winlen;    }
15     void push_back(const int& index,const int& _val)
16     {
17         while(head <= tail && val[tail] < _val)
18             tail--;
19         pos[++tail] = index;
20         val[tail] = _val;
21         while(index - pos[head] >= winlen)
22             head++;
23     }
24     int top()
25     {    return val[head];    }
26 }q;
27 
28 int main()
29 {
30     while(scanf("%d %d", &V, &N) != EOF)
31     {
32         for(int i=1 ; i<=N ; i++)
33             scanf("%d %d", &n[i], &c[i]);
34         memset(f, 0, sizeof(f));
35         for(int i=1 ; i<=N ; i++)
36         {
37             if(n[i] == 0)    continue;
38             else if(n[i] == 1)
39                 for(int j=V ; j>=c[i] ; j--)
40                     f[j] = max(f[j], f[j-c[i]]+c[i]);
41             else if(n[i] > V / c[i])
42                 for(int j=c[i] ; j<=V ; j++)
43                     f[j] = max(f[j], f[j-c[i]]+c[i]);
44             else
45             {
46                 for(int d=0 ; d<c[i] ; d++)
47                 {
48                     q.reset(n[i]+1);
49                     for(int j=0 ; j<=(V-d)/c[i] ; j++)
50                     {
51                         q.push_back(j, f[j*c[i]+d]-j*c[i]);
52                         f[j*c[i]+d] = q.top() + j*c[i];
53                     }
54                 }
55             }
56         }
57         printf("%d\n", f[V]);
58     }
59 }
View Code

理解单调队列优化多重背包的方法,可以看以下两篇文章,比较详细:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html

http://wenku.baidu.com/view/2655b60476c66137ee0619b4.html

我的理解:

  1. 原dp方程是:dp[j]=max{dp[j-k*v[i]]+k*w[i]}  (1<=k<=m[i])。首先要清楚为什么原dp方程不能用单调队列优化:因为k的范围:因为k的范围是与j无关的,必须要化简成f(j)<=k<j之类的形式。
  2. 那么经过化简(方法见以上两篇文章):

    dp[λ*c[i]+d] = max(dp[kk*c[i]+d] - kk*w[i]) + λ*w[i]    

    λ表示商,d表示余数,λ*c[i]+d表示原来的j,kk=λ-原来的k,且kk的范围是[λ-n[i], λ]

    这样就满足了单调队列优化的形式,λ*w[i]是不动量,dp[kk*c[i]+d] - kk*w[i]是放入队列中的值,k满足[λ-n[i], λ],所 以要把λ从小到大循环,注意[λ-n[i], λ]表示原来的j,而插入队列pos中的是λ!!

     3.  要把多重背包中01背包和完全背包的情况分开处理一下,算是一种优化

很适合理解单调队列优化多重背包的一道题~ 

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

导航