分组背包问题的3种形式:每组最多选1个(模板),必须选1个,至少选1个
每组最多选1个 有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。 每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。接下来有 N 组数据:每组数据第一行有一个整数 $Si$,表示第 i 个物品组的物品数量;每组数据接下来有 $S_i$ 行,每行有两个整数 $v {ij},w_{ij}$,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;输出格式
输出一个整数,表示最大价值。
数据范围
$0<N,V≤100$, $0<Si≤100$, $0<v {ij}, w_{ij}≤100$,
代码
没有优化一维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std;const int N = 1010 ;int n,m;int v[N][N],w[N][N],s[N];int f[N][N];int main () { cin>>n>>m; for (int i=1 ;i<=n;i++) { cin>>s[i]; for (int j=0 ;j<s[i];j++) { cin>>v[i][j]>>w[i][j]; } } for (int i=1 ;i<=n;i++) { for (int j=0 ;j<=m;j++) { f[i][j]=f[i-1 ][j]; for (int k=0 ;k<s[i];k++) { if (j>=v[i][k]) f[i][j]=max (f[i][j],f[i-1 ][j-v[i][k]]+w[i][k]); } } } cout<<f[n][m]<<endl; }
用逆序更新优化一维数组,由于可以不选,才可以优化掉一维数组,f[i][j]=f[i-1][j],只有一维数组就不需要继承上一次的状态了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std;const int N = 1010 ;int n,m;int v[N][N],w[N][N],s[N];int f[N];int main () { cin>>n>>m; for (int i=1 ;i<=n;i++) { cin>>s[i]; for (int j=0 ;j<s[i];j++) { cin>>v[i][j]>>w[i][j]; } } for (int i=1 ;i<=n;i++) for (int j=m;j>=0 ;j--) for (int k=0 ;k<=s[i];k++) if (j>=v[i][k]) f[j]=max (f[j],f[j-v[i][k]]+w[i][k]); cout<<f[m]<<endl; }
每组必须选1个 小红希望出一场题目,但是他的实力又不够,所以他想到可以从以前的比赛中各抽一题,来组成一场比赛。不过一场比赛的难度应该是有限制的,所以所以这一场比赛会给一个目标难度分数 $target$。
小红选 $n$ 场比赛,每场 $m$ 个题,小红会从每一场选一道题,使其组成的题的难度分数尽量接近 $target$。小红想知道挑选的题的难度分数与 $target$ 相差的最小值是多少。
输入描述:
第一行输入两个整数 $n,m (1≤n≤100,1≤m≤20)$代表小红选了 $n$ 场比赛,每场比赛有 $m$ 道题。 此后 $n$ 行,第 $i$ 行输入 $m$ 个整数 $a{i,j} (1≤a {i,j}≤50)$代表第 $i$ 场比赛的第 $j$ 道题的难度分数。 最后一行输入一个整数 $target (1≤target≤5000) $代表小红希望这一场比赛的难度分数。
输出描述:
在一行上输出一个整数,表示挑选的题的难度分数与 $target$ 相差的最小值。
示例1
输入
1 2 3 4 5 3 3 1 4 7 2 5 8 3 6 9 10
输出
说明
1 小红可以选第一场比赛的第一道题,第二场比赛的第一道题,第三场比赛的第二道题,这样挑选的题的难度分数为 1+2+6=9,与target 相差的最小值为 1。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <bits/stdc++.h> using namespace std;typedef long long ll;int n, m, target;int a[105 ][25 ];bool f[2 ][5005 ]; signed main () { cin >> n >> m; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { cin >> a[i][j]; } } f[0 ][0 ] = true ; for (int i = 0 ; i < n; i++) { memset (f[(i+1 )%2 ], false , sizeof f[(i+1 )%2 ]); for (int j = 0 ; j <= 5000 ; j++) { for (int k = 0 ; k < m; k++) { if (j - a[i][k] >= 0 ) { f[(i+1 )%2 ][j] |= f[i%2 ][j-a[i][k]]; if (f[(i+1 )%2 ][j]) { break ; } } } } } cin >> target; int ans = INT_MAX; for (int j = 0 ; j <= 5000 ; j++) { if (f[n%2 ][j]) ans = min (ans, abs (target - j)); } cout << ans << endl; return 0 ; }
每组至少选1个 hdu3033
注意循环嵌套顺序变了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> #include <cstdio> #include <cstring> using namespace std;const int maxn = 105 ;int num[11 ],cost[11 ][maxn],value[11 ][maxn];int n,m,k,dp[11 ][10005 ];int main () { int a,b,c; while (scanf ("%d%d%d" ,&n,&m,&k)!=EOF) { memset (num,0 ,sizeof (num)); memset (dp,-1 ,sizeof (dp)); for (int i = 1 ; i <= n; i++) { scanf ("%d%d%d" ,&a,&b,&c); num[a]++; cost[a][num[a]] = b; value[a][num[a]] = c; } memset (dp[0 ],0 ,sizeof (dp[0 ])); for (int i = 1 ; i <= k; i++) { for (int j = 1 ; j <= num[i]; j++) { for (int v = m; v >= cost[i][j]; v--) { if (dp[i][v-cost[i][j]] != -1 ) dp[i][v] = max (dp[i][v],dp[i][v-cost[i][j]]+value[i][j]); if (dp[i-1 ][v-cost[i][j]] != -1 ) dp[i][v] = max (dp[i][v],dp[i-1 ][v-cost[i][j]]+value[i][j]); } } } if (dp[k][m] == -1 ) printf ("Impossible\n" ); else printf ("%d\n" ,dp[k][m]); } return 0 ; }