2018.8.9T1(状压DP)

描述
作为一场自闭模拟赛,需要一道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 (1u<vn) ( 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


2nn 2 n ∗ n 的50分做法很好想到
很多人把这个50分做法当成满分做法直接交了

一个最显然的问题就是杜老师出题怎么会这么水呢
而且 2nn 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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值