背包问题我现在初学几个简单的,一个是01背包,一个是完全背包,还有一个是多重背包。
他们三者可以相互转化,主要基础就是01背包和完全背包。
具体内容可以去看背包九讲。
给几个例题:
A - Piggy-Bank
这个是一个裸的01背包
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <string> #include <algorithm> #define inf64 0x3f3f3f3f3f3f3f3f #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 10; int dp[maxn]; int c[maxn], val[maxn]; int main() { int t; scanf("%d", &t); while(t--) { int w1, w2; scanf("%d%d", &w1, &w2); int w = w2 - w1; int n; scanf("%d", &n); memset(dp, inf, sizeof(dp)); dp[0] = 0; for (int i = 1; i <= n; i++) scanf("%d%d", &val[i], &c[i]); for(int i=1;i<=n;i++) { for(int j=c[i];j<=w;j++) { dp[j] = min(dp[j], dp[j - c[i]]+val[i]); } } if (dp[w] >= inf) printf("This is impossible.\n"); else printf("The minimum amount of money in the piggy-bank is %d.\n", dp[w]); } return 0; }
I - Coins
这个是一个裸的多重背包
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <string> #include <algorithm> #define inf64 0x3f3f3f3f3f3f3f3f #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e6 + 10; int num[maxn], dp[maxn], val[maxn]; int N,V; void zero(int weight,int value) { for(int i=V;i>=weight;i--) { dp[i] = max(dp[i], dp[i - weight] + value); } } void all(int weight,int value) { for(int i=weight;i<=V;i++) { dp[i] = max(dp[i], dp[i - weight] + value); } } void solve() { int t = 1; int ncount = 0; for(int i=1;i<=N;i++) { if (num[i] * val[i] >= V) all(val[i], val[i]); else { t = 1; ncount = num[i]; while(t<=ncount) { zero(t*val[i], t*val[i]); ncount -= t; t *= 2; } zero(ncount*val[i], ncount*val[i]); } } } int main() { while(cin>>N>>V&&N!=0&&V!=0) { for (int i = 1; i <= N; i++) scanf("%d", &val[i]); for (int i = 1; i <= N; i++) scanf("%d", &num[i]); memset(dp, -inf, sizeof(dp)); dp[0] = 0; solve(); int ans = 0; for(int i=1;i<=V;i++) { if (dp[i] > 0) ans++; //printf("dp[%d]=%d\n",i, dp[i]); } printf("%d\n", ans); } return 0; }
C - Dollar Dayz
这个是一个裸的01背包,不过这个有一点点不一样,这个dp记录的是方案数。
这个还要注意,因为这个和顺序无关,只要你把东西放入背包,所以这个时候枚举的顺序要注意
先枚举容量,再枚举物品;如果和顺序有关就要先枚举物品再枚举容量。
这个题目要先枚举容量。
除此之外还要注意这个会爆long long
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <string> #include <algorithm> #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e4 + 10; typedef long long ll; const ll inf64 = 1000000000000000000; ll a[maxn], b[maxn]; int main() { int n, k; while(scanf("%d%d", &n, &k)!=EOF) { memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b)); b[0] = 1; for(int i=1;i<=k;i++) { for(int j=i;j<=n;j++) { a[j] = a[j] + a[j - i] + (b[j] + b[j - i]) / inf64; b[j] = (b[j] + b[j - i]) % inf64; } } if (a[n] == 0) printf("%lld\n", b[n]); else printf("%lld%018lld\n", a[n], b[n]); } return 0; }
E - Charlie's Change
这个是一个01背包+输出路径,这个路径的输出可以看背包九讲。
#include <cstdio> #include <cstdlib> #include <cstring> #include <queue> #include <algorithm> #include <vector> #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 10; int dp[maxn], used[maxn], path[maxn]; int weight[4] = { 1,5,10,25 }; int main() { int n, num[5]; while(scanf("%d%d%d%d%d",&n,&num[0],&num[1],&num[2],&num[3])!=EOF) { if (n == 0 && num[0] == 0 && num[1] == 0 && num[2] == 0 && num[3] == 0) break; memset(dp, -inf, sizeof(dp)); memset(path, 0, sizeof(path)); dp[0] = 0; path[0] = -1; for(int i=0;i<4;i++) { memset(used, 0, sizeof(used)); for(int j=weight[i];j<=n;j++) { if(dp[j-weight[i]]+1>dp[j]&&dp[j-weight[i]]>=0&&used[j-weight[i]]<num[i]) { dp[j] = dp[j - weight[i]] + 1; path[j] = j - weight[i]; used[j] = used[j - weight[i]] + 1; } } } if (dp[n] < 0) printf("Charlie cannot buy coffee.\n"); else { int ans[100]; memset(ans, 0, sizeof(ans)); while(path[n]!=-1) { ans[n - path[n]]++; n = path[n]; } printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", ans[weight[0]], ans[weight[1]], ans[weight[2]], ans[weight[3]]); } } return 0; }
F - The Fewest Coins
这个是一个多重背包和完全背包的混合,需要进行一定的转化
#include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #include <vector> #include <algorithm> #define inf 0x3f3f3f3f using namespace std; const int maxn = 2e5 + 10; int N, V; int weight[maxn], num[maxn]; int f1[maxn], f2[maxn], V1; void zeroonepack(int weight, int val, int f[]) { for (int i = V; i >= weight; i--) { f[i] = min(f[i], f[i - weight] + val); } } void completepack(int weight, int val, int f[]) { for (int i = weight; i <= V; i++) { f[i] = min(f[i], f[i - weight] + val); } } void multiplepack(int weight, int val, int count, int f[]) { if (count*weight >= V) completepack(weight, val, f); else { int t = 1; while (t < count) { zeroonepack(weight*t, val*t, f); count -= t; t *= 2; } zeroonepack(count*weight, count*val, f); } } int main() { while (scanf("%d%d", &N, &V1) != EOF) { int max_val = 0; for (int i = 1; i <= N; i++) { scanf("%d", &weight[i]); max_val = max(max_val, weight[i]); } for (int i = 1; i <= N; i++) scanf("%d", &num[i]); V = max_val * max_val + V1 + 10; memset(f1, inf, sizeof(f1)); memset(f2, inf, sizeof(f2)); f1[0] = 0, f2[0] = 0; for (int i = 1; i <= N; i++) { multiplepack(weight[i], 1, num[i], f1);//顾客 } for (int i = 1; i <= N; i++) { completepack(weight[i], 1, f2); } //printf("v=%d v1=%d\n", V, V1); int ans = inf; for (int i = 0; i <= V - V1; i++) { if (f1[V1 + i] != inf && f2[i] != inf) { ans = min(f1[V1 + i] + f2[i], ans); } } if (ans != inf) printf("%d\n", ans); else printf("-1\n"); } return 0; }