PKU 1276 Cash Machine
题目见此:http://poj.grids.cn/practice/1276
解题思路:
- 一看就是多重背包,没什么说的了……
- 一开始不会单调队列优化,后来写了个优化了,可以对照着看看,加深理解
未用单调队列优化:
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 }
单调队列优化:
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 }
理解单调队列优化多重背包的方法,可以看以下两篇文章,比较详细:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html
http://wenku.baidu.com/view/2655b60476c66137ee0619b4.html
我的理解:
- 原dp方程是:dp[j]=max{dp[j-k*v[i]]+k*w[i]} (1<=k<=m[i])。首先要清楚为什么原dp方程不能用单调队列优化:因为k的范围:因为k的范围是与j无关的,必须要化简成f(j)<=k<j之类的形式。
- 那么经过化简(方法见以上两篇文章):
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背包和完全背包的情况分开处理一下,算是一种优化
很适合理解单调队列优化多重背包的一道题~
浙公网安备 33010602011771号