Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2964
Algorithm:
一道很新颖的背包问题
此题每个状态要维护的量巨多,而转移方式也巨多,直接写转移方程明显是不现实的
那要考虑的就是能否将这些量分开考虑,它们间是否有明确的约束关系
可以发现mp和sp的相关操作和在哪一个回合无关,而只与需要的回合总数有关
那么就先DP出fm[i]和fs[i],分别表示用i个回合能用mp与sp打出的最大伤害,可以算出最少要mneed个回合战胜boss
接下来再逐个回合考虑hp问题,用dp[i][j]表示到第i个回合血量为j时能挤出的最大回合数
能挤出mneed个回合则YES,能在第n回合保持不死则输出Tie,否则NO
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=1000+10; const int INF=1<<27; int T,n,m,x,hp,mp,sp,mp_cnt,sp_cnt,dhp,dsp,dmp; int a[MAXN],ump[MAXN],amp[MAXN],usp[MAXN],asp[MAXN]; int fm[MAXN],gm[MAXN][MAXN],fs[MAXN],gs[MAXN][MAXN],dp[MAXN][MAXN]; void up(int &x,int y){if(x<y) x=y;} void solve() { memset(fm,0,sizeof(fm));memset(gm,0,sizeof(gm)); memset(fs,0,sizeof(fs));memset(gs,0,sizeof(gs)); cin>>n>>m>>hp>>mp>>sp>>dhp>>dmp>>dsp>>x; for(int i=1;i<=n;i++) cin >> a[i]; cin >> mp_cnt; for(int i=1;i<=mp_cnt;i++) cin >> ump[i] >> amp[i]; cin >> sp_cnt; for(int i=1;i<=sp_cnt;i++) cin >> usp[i] >> asp[i]; for(int i=0;i<=n;i++) { for(int j=0;j<=mp;j++) up(fm[i],gm[i][j]); if(i==n) break; for(int j=0;j<=mp;j++) { up(gm[i+1][min(mp,j+dmp)],gm[i][j]); for(int k=1;k<=mp_cnt;k++) if(j>=ump[k]) up(gm[i+1][j-ump[k]],gm[i][j]+amp[k]); } } for(int i=0;i<=n;i++) { for(int j=0;j<=sp;j++) up(fs[i],gs[i][j]); if(i==n) break; for(int j=0;j<=sp;j++) { up(gs[i+1][min(sp,j+dsp)],gs[i][j]+x); for(int k=1;k<=sp_cnt;k++) if(j>=usp[k]) up(gs[i+1][j-usp[k]],gs[i][j]+asp[k]); } } int mneed=INF; for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) if(fm[i]+fs[j]>=m) mneed=min(mneed,i+j); for(int i=0;i<MAXN;i++) for(int j=0;j<MAXN;j++) dp[i][j]=-INF; dp[1][hp]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=hp;j++) if(dp[i][j]>=mneed) { cout << "Yes " << i << endl; return; } for(int j=1;j<=hp;j++) { if(min(hp,j+dhp)>a[i]) up(dp[i+1][min(hp,j+dhp)-a[i]],dp[i][j]); if(j-a[i]>0) up(dp[i+1][j-a[i]],dp[i][j]+1); } } for(int i=1;i<=hp;i++) if(dp[n+1][i]>=0) { cout << "Tie" << endl; return; } cout << "No" << endl; } int main() { cin >> T; while(T--) solve(); return 0; }
Review:
当发现转移状态时要维护的信息过多时,可以查看这些量是否可以分离
先逐个转移,最后再根据较弱的约束将状态加合
注意大小写啊!!