2829: 闯关游戏
题意:
思路:
看起来像是分组背包题,但是一旦往分组背包方向想就会寄。因为分组背包是每一组最多选一个,而这个题是必须选一个,所以还是要自己推一个 DP 公式。
定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 为前 i i i 关花费恰好是 j j j 的最大价值,那么初始 d p [ 0 ] [ 0 dp[0][0 dp[0][0 ~ m ] = 0 , − 1 , − 1 , − 1 ⋯ − 1 m]={0, -1, -1,-1 \dots -1} m]=0,−1,−1,−1⋯−1 ,-1 代表是无效状态,不能推到其他的状态。转移方程就是 d p [ i ] [ j ] = max ( d p [ i − 1 ] [ j − v ] + w ) ( 当 d p [ i − 1 ] [ j − v ] ! = − 1 时 ) dp[i][j]=\max(dp[i-1][j-v]+w)(当dp[i-1][j-v]!=-1时) dp[i][j]=max(dp[i−1][j−v]+w)(当dp[i−1][j−v]!=−1时) 。
另外 d p [ i ] [ j ] dp[i][j] dp[i][j] 也可以定义成前 i i i 关花费上限是 j j j 的最大价值。
AC代码:http://acm.zzuli.edu.cn/showsource.php?id=6206359
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
#define endl '\n'
#define fi first
#define se second
#define ppb pop_back
#define pb push_back
#define ios ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define mset(x, a) memset(x, a, sizeof (x))
#define rep(i, l, r) for(LL i = l; i <= (r); ++ i)
#define per(i, r, l) for(LL i = r; i >= (l); -- i)
#define reps(i, l, r, d) for(LL i = l; i <= (r); i += d)
#define pers(i, r, l, d) for(LL i = r; i >= (l); i -= d)
template<class T> bool ckmax(T& a, T b) { return a < b ? (a = b, 1) : 0; }
template<class T> bool ckmin(T& a, T b) { return a > b ? (a = b, 1) : 0; }
const int N = 2e5 + 10, M = 1e5 + 10;
const LL p = 1e9 + 7;
const LL inf = 0x3f3f3f3f3f3f3f3f;
LL n, h, dp[2][N];
void solve()
{
cin >> n >> h;
LL ans = 0;
rep(i, 0, h) dp[0][i] = dp[1][i] = -1;
dp[0][0] = 0;
rep(i, 1, n){
LL a, b, c, d; cin >> a >> b >> c >> d;
rep(j, 0, h){
dp[i & 1][j] = -1;
if(j - a >= 0 && dp[i + 1 & 1][j - a] != -1) ckmax(dp[i & 1][j], dp[i + 1 & 1][j - a] + b);
if(j - c >= 0 && dp[i + 1 & 1][j - c] != -1) ckmax(dp[i & 1][j], dp[i + 1 & 1][j - c] + d);
ckmax(ans, dp[i & 1][j]);
}
}
cout << ans << endl;
}
int main()
{
ios;
int T; cin >> T;
while(T -- )
solve();
return 0;
}