描述
作为一场自闭模拟赛,需要一道10k模拟加玄学剪枝题。
在魔塔这个游戏中,勇士有四个数值,血量,攻击,防御和魔防。怪物也有三个数值,血量,攻击和防御。勇士和怪物攻击方式一般是这样的,双方轮流攻击,每回合造成自己攻击减去对方防御的伤害,如果自己的攻击比对方的防御低,那么无法对对方造成伤害。如果怪物血量变得小于等于0,那么怪物死亡,勇士获胜。如果勇士的攻击不超过怪物的防御,那么无法战斗。在一场战斗后,如果怪兽造成的总伤害超过了自己的魔防,那么消耗的血量为总伤害减去自己的魔防值,否则伤害为0。勇士必须保证战斗之后剩余的血量大于0。
一般来说,都是勇士先攻击。但是怪物有一些特殊属性:
先攻:第一个回合由怪物先攻击。
魔攻:怪物会无视勇士的防御,你可以认为在战斗的时候勇士的防御为00。
二连击:怪物每回合攻击两次。
模仿:怪物的攻防与勇士的攻防相同。
这四个特殊属性都是可以任意叠加的。
这个魔塔里面有nn个怪,每个怪后面都守着一个宝石,每个宝石都能对血量,攻击,防御和魔防这些数值有所增益。同时打怪之间有一些先后顺序,比如说如果怪vv被怪uu挡住了,那么我们只能先打uu再打vv。所以我们会有kk条规则,表示uu这个怪必须在vv之前打。
你可以需要按一定顺序去清怪和吃宝石,吃宝石之前要求守着这个宝石的怪被杀死。
请问杀完所有的怪剩下的最多能剩多少血量。
输入格式
第一行一个整数T,表示数据组数。
每组数据第一行四个整数,表示h,a,d,m(1≤h≤109,1≤a,d≤105,0≤m≤105)表示勇士的初始血量,攻击,防御与魔防。
接下来一行一个数字n,表示怪的数量。
接下来n行,每行八个数字,表示H,A,D,S,ap,dp,mp,hp(1≤H,A,D≤105,0≤S≤15,0≤ap,dp,mp≤104,1≤hp≤109),表示怪物的血量,攻击,防御,属性,以及怪物守的宝石能增加的攻击,防御,魔防和血量。
怪物的属性由二进制表示,如果有先攻属性,那么S增加1,魔攻增加2,二连击增加4,模仿增加8。
接下来一行一个整数k,表示有k条规则。接下来kk行,每行两个正整数u,v (1≤u<v≤n) ( 1 ≤ u < v ≤ n ) ,表示怪u必须在怪v之前杀死。
输出格式
对于每组数据输出一行,表示杀完所有的怪剩下的最多血量。如果无法清完所有怪,那么输出−1−1。
样例1
样例输入
1
20 2 2 0
10
2 2 0 0 2 0 0 80
18 8 0 0 0 0 0 20
40 6 1 0 2 0 0 40
42 7 3 0 1 0 0 40
10 10 5 0 0 2 0 40
25 5 0 0 0 0 0 20
35 4 1 0 0 1 0 20
60 7 2 0 1 1 0 40
32 8 2 0 0 0 0 60
160 15 0 0 0 0 0 0
9
1 2
2 3
1 4
4 5
1 6
6 7
6 8
7 9
4 10
样例输出
1
限制与规定
对于 20%20% 的数据,有 1≤n≤5,k=0,所有怪物没有特殊属性。
对于 40% 的数据,有 1≤n≤8,k=0,所有怪物没有特殊属性。
对于另外 20% 的数据,有 1≤n≤14,k≤100所有怪物没有特殊属性。
对于另外 20% 的数据,有 1≤n≤8,k≤100
对于 100% 的数据,有 1≤n≤14,k≤100,T≤10
时间限制:10s
空间限制:512MB
2n∗n
2
n
∗
n
的50分做法很好想到
很多人把这个50分做法当成满分做法直接交了
一个最显然的问题就是杜老师出题怎么会这么水呢
而且
2n∗n
2
n
∗
n
怎么会放14的数据呢
显然这样是错的
问题就出在怪物的模仿技能,杀完怪物直接吃宝石不一定是最优的
比如说你把自己吃的贼屌,可能一个模仿怪物就把你秒了
所以我们要把怪物压进状态
0表示没杀怪物,1表示杀了怪物没吃宝石,2表示吃了宝石
转移的时候枚举每一位进行转移即可
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
const ll INF = 1000000000000000000ll;
ll dp[5000000];
bool check[5000000];
int H,A,D1,D2;
int n , k;
int ms;
ll mi[20];
vector<int>attack[20];
struct monster
{
ll h,a,d1;
int kind;
int ap,d1p,d2p,hp;
}a[20];
struct have{
ll a,d1,d2;
}has[5000000];
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
int get(int s,int pl)
{
while(pl)
{
s /= 3;
pl--;
}
return s % 3;
}
void pre()
{
rep(i,0,ms)
{
int now = i;
has[i].a = A;has[i].d1 = D1;has[i].d2 = D2;
rep(j,1,n)
{
int x = now % 3;
if(x == 2)
{
has[i].a += a[j].ap;
has[i].d1 += a[j].d1p;
has[i].d2 += a[j].d2p;
}
now /= 3;
}
}
rep(i,0,ms)
{
check[i] = true;
int now = i;
bool ffff= true;
rep(j,1,n)
{
int x = now % 3;
now /= 3;
if(x == 1 || x == 2)
rep(t,1,attack[j].size())
if( !( get(i,attack[j][t-1]-1) % 3 ) )
{
ffff = false;
break;
}
if(!ffff) {check[i] = false;break;}
}
if(!ffff) continue;
}
return;
}
ll Cost(int i,int j)
{
ll re = 0;
int n = j;
n--;
int now = i;
while(n)
{
now /= 3;
n--;
}
{
re = 0;
if(!check[i]) return INF;
int kind = a[j].kind;
bool flag = false , fff = false , flag2 = false;
if(kind & 1) fff = true;
kind /= 2;
if(kind & 1) flag2 = true;
kind /= 2;
if(kind & 1) flag = true;
kind /= 2;
int h = a[j].h;
int times;
if(kind & 1)
{
if(has[i].a <= has[i].d1) return INF;
times = h / (has[i].a - has[i].d1);
if( 1ll*(has[i].a - has[i].d1) * times == h ) times--;
if(fff) times++;
if(!flag)
{
if(!flag2) re = 1ll*times*(has[i].a-has[i].d1)-has[i].d2;
else re = 1ll*times*has[i].a - has[i].d2;
}
else
if(!flag2) re = 1ll*times*2*(has[i].a-has[i].d1)-has[i].d2;
else re = 1ll*times*2*has[i].a-has[i].d2;
}
else
{
if(has[i].a <= a[j].d1) return INF;
times = h / (has[i].a - a[j].d1);
if( 1ll*(has[i].a - a[j].d1) * times == h ) times--;
if(fff) times++;
if(!flag)
{
if(!flag2) re = 1ll*times * (a[j].a - has[i].d1) - has[i].d2;
else re = 1ll * times * a[j].a - has[i].d2;
}
else
if(!flag2) re = 1ll* times * 2*(a[j].a - has[i].d1) - has[i].d2;
else re =1ll* times * 2*a[j].a - has[i].d2;
}
now /= 3;
if(re < 0) re = 0;
}
return re;
}
void Dp()
{
rep(s,1,ms)
{
if(!check[s])
{
dp[s] = -1;
continue;
}
int now = s;
rep(i,1,n)
{
int x = now % 3;
now /= 3;
if(x == 0) continue;
if(x == 1)
{
if(dp[s-mi[i-1]] != -1)
dp[s] = max(dp[s],dp[s-mi[i-1]] - Cost(s-mi[i-1],i));
}
else
if(dp[s-mi[i-1]] != -1)
dp[s] = max(dp[s],dp[s-mi[i-1]]+a[i].hp);
}
if(dp[s] == 0) dp[s]--;
}
return;
}
void reset()
{
memset(dp,0,sizeof(dp));
rep(i,1,n) attack[i].clear();
}
void init()
{
int T = read();
while(T--)
{
H = read();A = read();D1 = read();D2 = read();
n = read();
mi[0] = 1;
rep(i,1,n) mi[i] = mi[i - 1] * 3;
ms = mi[n] - 1;
rep(i,1,n)
a[i].h = read() , a[i].a = read() , a[i].d1 = read(),
a[i].kind = read(), a[i].ap = read(),
a[i].d1p= read() , a[i].d2p = read() , a[i].hp = read();
k = read();
rep(i,1,k)
{
int x = read() , y = read();
attack[y].push_back(x);
}
pre();
dp[0] = H;
Dp();
printf("%lld\n",dp[ms]);
reset();
}
}
int main()
{
init();
return 0;
}