背包问题
一、01背包
给你一个容量为W的书包,一些体积(重量)为wi,价值为vi的东西,每个东西只有放与不放两种选择。
经典例题:HDU 2602
http://acm.hdu.edu.cn/showproblem.php?pid=2602
题解:背包容量为V,有N件物品,已知每件物品的体积(重量)与价值,求最大价值。
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 #include<stack> 7 #define ll long long 8 using namespace std; 9 const int N = 1005; 10 const int mod = 1e9 + 7; 11 int dp[N][N], v[N], w[N]; 12 13 int main() 14 { 15 std::ios::sync_with_stdio(false); 16 int T; 17 cin >> T; 18 while(T--){ 19 int n, V; 20 memset(dp, 0, sizeof(dp)); 21 22 cin >> n >> V; 23 for(int i = 1; i <= n; i++){ 24 cin >> v[i]; 25 } 26 for(int i = 1; i <= n; i++){ 27 cin >> w[i]; 28 } 29 //dp[i][j]表示在前i个物品中,总重量不超过j的总价值 30 for(int i = 1; i <= n; i++){ 31 for(int j = 0; j <= V; j++){//顺序 从0到V 32 if(j < w[i]){ 33 dp[i][j] = dp[i - 1][j];//放不下该物品,价值不变 34 } 35 else { 36 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);//放得下,求最大 37 } 38 } 39 } 40 41 cout << dp[n][V] << endl; 42 } 43 return 0; 44 }
二、完全背包
给你一个书包,和一些物品,物品价值和体积(重量)已知,数量无数,可以随便放。
经典例题:HDU 1114
http://acm.hdu.edu.cn/showproblem.php?pid=1114
题解:一个存钱罐重a,装满钱重b,给你一些硬币的价值和重量,求最大价值。
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #define ll long long 8 using namespace std; 9 10 const int N = 10005; 11 const int inf = 0x3f3f3f3f; 12 13 int main() 14 { 15 int T; 16 cin >> T; 17 18 while(T--) 19 { 20 int a, b, m; 21 int dp[N], v[N], w[N];//dp[j]表示在重量不超过j的情况下的最大价值 22 memset(dp, inf, sizeof(dp)); 23 dp[0] = 0; 24 25 cin >> a >> b >> m; 26 27 for(int i = 0; i < m; i++) 28 { 29 cin >> v[i] >> w[i]; 30 } 31 32 for(int i = 0; i < m; i++) 33 { 34 for(int j = w[i]; j <= b - a; j++) 35 { 36 dp[j] = min(dp[j], dp[j - w[i]] + v[i]); 37 } 38 } 39 40 if(dp[b - a] == inf) 41 cout << "This is impossible." << endl; 42 else 43 cout << "The minimum amount of money in the piggy-bank is " << dp[b - a] << '.' << endl; 44 45 } 46 return 0; 47 }
三、多重背包
给你一个书包,物品的价值和重量已知,每种物品的数量有限定且不一样。
可以转化成01背包和完全背包问题求解。
经典例题:HDU 2844
http://acm.hdu.edu.cn/showproblem.php?pid=2844
题解:给你几种硬币的价值和数量,求他们能组成多少种价值。
代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #define ll long long 8 using namespace std; 9 10 const int N = 100005; 11 const int inf = 0x3f3f3f3f; 12 int dp[N]; 13 int v[N], num[N]; 14 15 void CompletePack(int v, int w, int bag) //完全背包 16 { 17 for(int i = w; i <= bag; i++) // 顺序 18 { 19 dp[i] = max(dp[i], dp[i - w] + v); 20 } 21 } 22 23 void ZeroOnePack(int v, int w, int bag) //01背包 24 { 25 for(int i = bag; i >= w; i--) //逆序 26 { 27 dp[i] = max(dp[i], dp[i - w] + v); 28 } 29 } 30 31 void MultiplePack(int v, int w, int count, int limit)//多重背包 32 { 33 if(w * count >= limit)//背包足够大,转化为完全背包问题 34 { 35 CompletePack(v, w, limit); 36 return; 37 } 38 else//转化为01背包 39 { 40 int k = 1; 41 while(k <= count) 42 { 43 ZeroOnePack(k * v, k * w, limit); 44 count -= k; 45 k *= 2;//采用二进制思想 46 } 47 ZeroOnePack(count * v, count * w, limit); 48 return; 49 } 50 } 51 52 int main() 53 { 54 std::ios::sync_with_stdio(false); 55 56 int n, m; 57 while(cin >> n >> m && n && m) 58 { 59 memset(dp, -inf, sizeof(dp)); 60 dp[0] = 0; 61 62 for(int i = 0; i < n; i++) 63 { 64 cin >> v[i]; 65 } 66 for(int i = 0; i < n; i++) 67 { 68 cin >> num[i]; 69 } 70 71 for(int i = 0; i < n; i++) 72 { 73 MultiplePack(v[i], v[i], num[i], m); 74 } 75 76 int ans = 0; 77 for(int i = 1; i <= m; i++) 78 { 79 if(dp[i] > 0) 80 ans++; 81 } 82 83 cout << ans << endl; 84 85 } 86 return 0; 87 }