【BZOJ 2964】Boss单挑战
Description
某RPG游戏中,最后一战是主角单挑Boss,将其简化后如下:
主角的气血值上限为HP,魔法值上限为MP,愤怒值上限为SP;Boss仅有气血值,其上限为M。
现在共有N回合,每回合都是主角先行动,主角可做如下选择之一:
1. 普通攻击:减少对方X的气血值,并增加自身DSP的愤怒值。(不超过上限)
2. 法术攻击:共有N1种法术,第i种消耗Bi的魔法值,减少对方Yi的气血值。(使用时要保证MP不小于Bi)
3. 特技攻击:共有N2种特技,第i种消耗Ci的愤怒值,减少对方Zi的气血值。(使用时要保证SP不小于Ci)
4. 使用HP药水:增加自身DHP的气血值。(不超过上限)
5. 使用MP药水:增加自身DMP的魔法值。(不超过上限)
之后Boss会攻击主角,在第i回合减少主角Ai的气血值。
刚开始时气血值,魔法值,愤怒值都是满的。当气血值小于等于0时死亡。
如果主角能在这N个回合内杀死Boss,那么先输出“Yes”,之后在同一行输出最早能在第几回合杀死Boss。(用一个空格隔开)
如果主角一定会被Boss杀死,那么输出“No”。
其它情况,输出“Tie”。
Input
输入的第一行包含一个整数T,为测试数据组数。
接下来T部分,每部分按如下规则输入:
第一行九个整数N, M, HP, MP, SP, DHP, DMP, DSP, X。
第二行N个整数Ai。
第三行第一个整数N1,接下来包含N1对整数Bi, Yi。
第四行第一个整数N2,接下来包含N2对整数Ci, Zi。
Output
输出共包含T行,每行依次对应输出一个答案。
Sample Input
5 100 100 100 100 50 50 50 20
50 50 30 30 30
1 100 40
1 100 40
5 100 100 100 100 50 50 50 10
50 50 30 30 30
1 100 40
1 100 40
Sample Output
Tie
样例说明
对于第一个样例,主角的策略是:第一回合法术攻击,第二回合使用HP药水,第三回合特技攻击,第四回合普通攻击。
HINT
对于100%的数据:1 ≤ N ≤ 1000,1 ≤ M ≤ 1000000,1 ≤ HP,MP,SP ≤ 1000,N1,N2 ≤ 10,DHP,Ai ≤ HP,DMP,Bi ≤ MP,DSP,Ci ≤ SP,X,Yi,Zi ≤ 10000,1 ≤ T ≤ 10。
分析:
考虑到HP、MP、SP之间是相互独立的,且MP、SP是回合无关的(只与回合总数有关,与在哪一回合攻击无关),所以可以先分别对MP和SP进行DP,F[i][j]表示进行i次MP/SP的操作后,剩下MP/SP为j,对Boss造成的最大伤害,再记录G1[i]和G2[i]分别表示进行i次MP/SP的操作,对Boss造成的最大伤害。
枚举i、j,统计当G1[i]+G2[j]>=M时,i+j的最小值mincost,这就是打倒Boss的最少操作次数(与HP无关)。
最后再考虑HP,HP是和回合有关的,所以DP的时候要考虑回合。用F[i][j]表示在第i回合之前,还剩下的HP为j,最多有多少次MP/SP的操作次数,包括当前第i回合。
若存在F[i][j]>=mincost(i<=n、j>0),那么输出Yes,最小的i即为答案。
否则,判断No或Tie,在DP之前将F[i][j]初始化为极小的负数,若存在F[N + 1][j]>=0(j>0),则表示N回合之内主角可以不死,却也打不死Boss,即输出Tie,否则输出No。
代码:
1 #include <cstdio> 2 #include <cstring> 3 4 int n, m, hp, mp, sp, dhp, dmp, dsp, x; 5 int a[1010], kmp[1010][2], ksp[1010][2], km, ks; 6 int dp_mp[1010][1010], dp_sp[1010][1010], fm[1010], fs[1010]; 7 int f[1010][1010]; 8 9 int min(int A, int B) { return A < B ? A : B;} 10 void checkmin(int &A, int B) { A > B ? A = B : 0;} 11 void checkmax(int &A, int B) { A < B ? A = B : 0;} 12 13 int solve() 14 { 15 memset(dp_mp, 0, sizeof dp_mp); 16 memset(dp_sp, 0, sizeof dp_sp); 17 memset(fm, 0, sizeof fm); 18 memset(fs, 0, sizeof fs); 19 memset(f, 180, sizeof f); 20 scanf("%d%d%d%d%d", &n, &m, &hp, &mp, &sp); 21 scanf("%d%d%d%d", &dhp, &dmp, &dsp, &x); 22 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 23 scanf("%d", &km); 24 for (int i = 1; i <= km; i++) 25 scanf("%d%d", &kmp[i][0], &kmp[i][1]); 26 scanf("%d", &ks); 27 for (int i = 1; i <= ks; i++) 28 scanf("%d%d", &ksp[i][0], &ksp[i][1]); 29 for (int i = 0; i <= n; i++) 30 { 31 for (int j = 0; j <= mp; j++) 32 { 33 checkmax(fm[i], dp_mp[i][j]); 34 for (int k = 1; k <= km; k++) if (j >= kmp[k][0]) 35 checkmax(dp_mp[i + 1][j - kmp[k][0]], dp_mp[i][j] + kmp[k][1]); 36 checkmax(dp_mp[i + 1][min(j + dmp, mp)], dp_mp[i][j]); 37 } 38 } 39 for (int i = 0; i <= n; i++) 40 { 41 for (int j = 0; j <= sp; j++) 42 { 43 checkmax(fs[i], dp_sp[i][j]); 44 for (int k = 1; k <= ks; k++) if (j >= ksp[k][0]) 45 checkmax(dp_sp[i + 1][j - ksp[k][0]], dp_sp[i][j] + ksp[k][1]); 46 checkmax(dp_sp[i + 1][min(j + dsp, sp)], dp_sp[i][j] + x); 47 } 48 } 49 int mincost = 99999999; 50 for (int i = 0; i <= n; i++) 51 for (int j = 0; j <= n; j++) 52 if (fm[i] + fs[j] >= m) 53 checkmin(mincost, i + j); 54 f[1][hp] = 1; 55 for (int i = 1; i <= n; i++) 56 { 57 for (int j = 1; j <= hp; j++) 58 { 59 if (f[i][j] >= mincost) 60 return printf("Yes %d\n", i); 61 if (j > a[i]) checkmax(f[i + 1][j - a[i]], f[i][j] + 1); 62 if (min(j + dhp, hp) > a[i]) 63 checkmax(f[i + 1][min(j + dhp, hp) - a[i]], f[i][j]); 64 } 65 } 66 for (int j = 1; j <= hp; j++) 67 if (f[n + 1][j] >= 0) 68 return printf("Tie\n"); 69 printf("No\n"); 70 } 71 72 int main() 73 { 74 int t; 75 scanf("%d", &t); 76 while (t--) solve(); 77 }