【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

2
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

Yes 4
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 }
时间: 2024-08-15 08:42:40

【BZOJ 2964】Boss单挑战的相关文章

NewTrain1 T5: Boss单挑战

题目分析 (看到这种打怪的题,一般不是贪心就是DP...) 我们发现对于此题,状态太多以至于无法贪心,所以我们只好DP. 因为 魔法攻击 与 普通攻击和特技攻击 是相对独立的,所以可以分开来考虑. 令fm[i],fs[i]分别为 只使用魔法攻击 与 只是用普通攻击和特技攻击 到第i回合(结束)所能造成伤害的最大值(先不管本人死活). 再令 gm[i][j],gs[i][j]分别为到了第i回合(结束),魔法量/愤怒值剩下j点所能造成的最大伤害(先不管本人死活). 那么对于每个 fm[i]与fs[i

BZOJ 4825 [Hnoi2017]单旋

题解:LCT维护Splay形态 Splay后发现只会有几个点发生变化,用LCT维护一下就可以了 在Splay中维护siz 还可以用Splay维护DFS序,旋转后DFS序不变,深度以子树为单位变化 天真的我以为直接模拟Splay可以A掉QWQ #include<iostream> #include<cstdio> #include<cstring> #include<map> using namespace std; const int maxn=100009

浅谈游戏中BOSS设计的思路

对于大多数游戏来说,BOSS在其设计上都有着不可替代的作用,也是玩家印象最为深刻的一部分.近期自己也有在做BOSS的设计工作,有一些心得想要分享一下: 1.明确BOSS的设计目的 在设计之初,我们一定要想明白,设计这个BOSS的目的在于什么,一场BOSS战往往需要花费巨大的成本来制作:美术需要花费大量时间.经历制作与其相关的多种美术资源:开发需要编写大量代码来实现BOSS相对复杂的行为,以及实现对应动画.特效及相关音频资源.一场失败的BOSS战设计,不仅会花费大量的成本,还会令玩家感到十分反感.

Dagger——Android上的依赖注入框架

* 你也可以去Github查看这片文章 简介 在开发程序的时候,会用到各种对象,很多对象在使用之前都需要进行初始化.例如你要操作一个SharedPreference,你需要调用getSharedPreferences(String name,int mode)来获取一个对象,然后才能使用它.而如果这个对象会在多个Activity中被使用,你就需要在每个使用的场景中都写下同样的代码.这不仅麻烦,而且增加了出错的可能.dagger的用途就是:让你不需要初始化对象.换句话说,任何对象声明完了就能直接用

#前端的挑战——单页应用的体验

---恢复内容开始--- ##什么是单页应用所谓单页应用,指的是在一个页面上集成多种功能,甚至整个系统就只有一个页面(一个html),所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上. ##为什么会有单页应用我们知道ajax技术的产生,一部分原因就是为了让访问网页的用户在不刷新页面的情况下,在页面上查看数据的变更.我们可以说ajax提升了体验. 随着互联网的发展,浏览器端承载的业务愈发复杂,web前端早已不再是一个简单页面,ajax局部刷一刷的小玩意.动辄数十个子页面的应用市面上随处可

[BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)

[BZOJ 1879][SDOI 2009]Bill的挑战 Description Solution 1.考虑状压的方式. 方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关系,那么空间为26^50,爆内存: 方案2:把每一个串压起来,多开一维记录匹配字符,那么空间为nlen26,合法,但不便于状态的设计和转移: 方案3:把每一个串同一个位置的字符放在一起,用一个布尔数组记录与每一个小写字母的匹配关系,那么空间为26^15*len,爆内存: 方案4:把每一个串同一个位置

Aizu 2249Road Construction 单源最短路变形《挑战程序设计竞赛》模板题

King Mercer is the king of ACM kingdom. There are one capital and some cities in his kingdom. Amazingly, there are no roads in the kingdom now. Recently, he planned to construct roads between the capital and the cities, but it turned out that the con

BZOJ 3029 守卫者的挑战

一开始搞了两个dp数组,分别记到第i盘,胜j盘,拿到k个碎片和背包大小>=k的概率,然后寻思着把它们乘起来? 后来发现好像是错的....这个地方不能用乘法,这两件事情是相关的.(我瞎jb猜的...其实是样例没过) 然后发现只要一个数组就行了....收益就是+a[i],-1,问最后收益>=0,胜场>=l的方案数...然后直接dp. 卡内存差评. #include<iostream> #include<cstdio> #include<cstring> #

BZOJ 1879 [Sdoi2009]Bill的挑战 ——状压DP

本来打算好好写写SDOI的DP题目,但是忒难了, 太难了,就写的这三道题仿佛是可做的. 生在弱省真是兴奋. 这题目直接状压,f[i][j]表示匹配到i,状态集合为j的方案数,然后递推即可. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k