EOJ 1027 邮资的问题
http://acm.cs.ecnu.edu.cn/problem.php?problemid=1027
题意:给N种邮票,第 i 种邮票面值为c[i], 有amt[i]张,
问,用这些邮票,可以贴出多少面额不同的邮资。
思路:
一看就是须装满的多重背包(参见lrj背包九讲)。只不过问的是有多少不同面值,
也即有多少种不同容量的背包能被装满。
如果改成这样问:给定一个面额,有多少种贴法,那就和 poj 1837 差不多了。
具体的:
把每个邮票看成一个容量为c[i]的物品,dp[i][j]表示第i个物品放完后容量为j的背包的状态,
dp[i][j] = 0:未满,dp[i][j] >= 1, 装满。
那么:初始化:只有容量为0的在放之前已装满,故dp[i][0] = 1, i ∈ [0, n];
其余均不能装满,初始化为0.
状态转移:(这里顺便求了"容量为j的有几种贴法")
for(i:1~n)
for(k:0~amt[i]) //k次 0-1背包
for(j:c[i]~V) //V是可能的最大值。
dp[i][j] += dp[i-1][j-c[i]];
// 不知你是否注意到,这里并没有最优化问题,这里只是类dp的递推,因为结果是固定的。
代码如下:(已1维优化)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string> 4 #include <algorithm> 5 #include <string.h> 6 #include <stdlib.h> 7 #define MAX 4100 8 using namespace std; 9 10 int main() 11 { 12 //freopen("testin.txt", "r", stdin); 13 //freopen("testout.txt", "w", stdout); 14 15 int t; 16 cin >> t; 17 while(t--) 18 { 19 int n, V; 20 int c[25], amt[25]; 21 cin >> n; 22 for(int i=0; i<n; i++) 23 cin >> c[i]; 24 for(int i=0; i<n; i++) 25 cin >> amt[i]; 26 V = MAX; //我这里V取得比较随意,要小心。 27 int dp[MAX+5]; 28 memset(dp, 0, sizeof(dp)); 29 dp[0] = 1; 30 31 for(int i=0; i<n; i++) 32 for(int j=1; j<=amt[i]; j++) 33 for(int k=V; k>=c[i]; k--) 34 dp[k] += dp[k-c[i]]; 35 36 int cnt = 0; 37 for(int i=0; i<V; i++) 38 if(dp[i]) 39 cnt ++; 40 //cout << i << " " << dp[i] << endl; 41 cout << cnt << endl; 42 } 43 44 45 return 0; 46 }