题目:
给n个人物m个装备,d是最大花费。每个人物和装备都有一个攻击力a和花费b。满足如下条件情况下选出一些人物和装备得到最大的攻击力:
(1)人物数量>=装备数量
(2)人物花费+装备花费<=d
(3)人物数量<=5
数据范围:0<n,m≤300,25≤d≤138,1000≤a1≤15488,500≤a2≤2500,3≤b1,b2≤12
做法:
01背包变形。这个数据范围即使没有第三个限制也能做。
思路是这样的:
dp1[j][k]表示选出≤j件装备花费≤k时,最大攻击力和。
dp2[j][k]表示选出j个人物花费≤k时,最大攻击力和。
如果我们得到上面信息。那么答案for一遍统计就好了:
for (int j = 1; j <= min(n, 5); ++j){ for (int k = 0; k <= d; ++k){ if (dp2[j][k] != -1) ans = max(ans, dp2[j][k]+dp1[j][d-k]); } }
而dp1、dp2做2次背包dp即可。
转移:
代码:
#include <bits/stdc++.h> #define debug(a) cout << #a ": " << a << endl #define IOS ios::sync_with_stdio(false), cin.tie(0) using namespace std; typedef long long ll; const int N = 310; int n, m, sum; struct node{ int power, cost; }guy[N], tool[N]; int dp1[N][N], dp2[N][N]; int main(void){ IOS; cin >> n >> m >> sum; for (int i = 1; i <= n; ++i){ cin >> guy[i].power >> guy[i].cost; } for (int i = 1; i <= m; ++i){ cin >> tool[i].power >> tool[i].cost; } for (int i = 1; i <= m; ++i){ for (int j = i; j >= 1; --j){ for (int k = sum; k >= 0; --k){ dp1[j][k] = max(dp1[j][k], dp1[j-1][k]); if (k >= tool[i].cost) dp1[j][k] = max(dp1[j][k], dp1[j-1][k-tool[i].cost]+tool[i].power); } } } memset(dp2, -1, sizeof dp2); for (int i = 0; i <= sum; ++i) dp2[0][i] = 0; for (int i = 1; i <= n; ++i){ for (int j = i; j >= 1; --j){ for (int k = sum; k >= guy[i].cost; --k){ if (dp2[j-1][k-guy[i].cost] != -1) dp2[j][k] = max(dp2[j][k], dp2[j-1][k-guy[i].cost]+guy[i].power); } } } int ans = 0; for (int i = 1; i <= min(n, 5); ++i){ for (int j = 0; j <= sum; ++j){ if (dp2[i][j] == -1) continue; ans = max(ans, dp2[i][j]+dp1[min(i, m)][sum-j]); } } cout << ans << endl; return 0; }