ZOJ 3719 Diablo III 动态规划

原题:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5042

题意:

游戏中敌人分为小怪和boss两种,杀一个小怪可以叠一层buff,最高5层,叠满buff时杀死boss可以掉史诗,同时buff归0

现在有N个敌人,必须依次击杀,当然也可以跳过,如果敌人是boss,则有血量h[i], 攻击力d[i]

我放英雄的血量是H,攻击为D,可以无限嗑药,每次加血量为P

与小怪战斗可以秒杀,和boss战斗时,双方初始满血,每回合我方英雄先手攻击,同时每回合可以选择嗑药或者不磕

问最多能获得多少件史诗,在此情况下最少要磕多少药


解题思路:

首先要解决的问题是,已知我放英雄和boss的属性,求最少要嗑多少药才能杀死boss

设dp状态 dp[hero][boss][ack] 表示我方英雄当前血量是hero,boss血量为boss,boss攻击力为ack的情况下,最少还要嗑多少药才能杀死boss

若 D >= boss,则本回合可击杀boss, dp[hero][boss][ack] = 0;

然后考虑嗑药的时间,可以选择在被boss攻击前嗑药,也可以在被boss攻击后嗑药或不嗑药,当然前提是挨揍之后不会死....

不嗑药时 dp[hero][boss][ack] = dp[hero-ack][boss-D][ack]

先嗑药时 dp[hero][boss][ack] = dp[min(hero + P, H)-ack][boss-D][ack] + 1

后嗑药时 dp[hero][boss][ack] = dp[min(hero - ack + P, H)][boss-D][ack] + 1

这里需要注意的是嗑药之后不能把HP上限给爆了,同时不要陷入嗑药一定先嗑的误区...

另外,如果先嗑药都抗不住boss的攻击的话,就挂了...


如果解决了上述问题,接下来的问题就不难了,要根据已有的敌人信息求最多能得多少史诗装备

比较容易想到的是dp[i][j][k]表示在前i个敌人得到j个装备且当前buff为k层,最少要磕多少药

但是N有5W,状态开不下,所以我们倒过来考虑,令p[i][j] 表示在击杀敌 i 之前,buff已经有了j层,则到最后最多可以获得多少史诗装备

同时k[i][j]表示对应状态要嗑多少药, 状态数是 5W * 6 因为buff最多就5层嘛

然后对于每个状态,显然有 p[i][j] = p[i + 1][j + k] 当i是boss的时候,k == 0,否则k == 1,同时 j + k <= 5

最后对于每个boss,可以再更新 p[i][5] 和 k[i][5]

最终的答案就是 p[1][0] 和 k[1][0],细节可以参考代码


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;

#define LL long long
#define ULL unsigned long long
#define mod 1000000007
#define eps 1e-8
#define MP make_pair

int dp[1001][1001][11], H, D, P;

int F( int hero, int boss, int ack ) {
    if( dp[hero][boss][ack] != -1 ) return dp[hero][boss][ack];
    int &ret = dp[hero][boss][ack];
    if( D >= boss ) return ret = 0;
    if( min(hero + P, H) <= ack ) return ret = 0x3f3f3f3f;
    ret = 0x3f3f3f3f;
    if( hero > ack ) {
        ret = min(ret, F(hero - ack, boss - D, ack));
        ret = min(ret, F(min(hero - ack + P, H), boss - D, ack) + 1);
    }
    ret = min(ret, F(min(hero + P, H) - ack, boss - D, ack) + 1);
    return ret;
}

int b[50005], h[50005], d[50005], n;
int p[50005][6], k[50005][6];

void solve() {
    memset(dp, -1, sizeof(dp));
    memset(p, 0, sizeof(p));
    memset(k, 0, sizeof(k));
    for( int i = n; i >= 1; --i ) {
        for( int j = 0; j < 5; ++j ) {
            p[i][j] = p[i+1][j+1-b[i]];
            k[i][j] = k[i+1][j+1-b[i]];
        }
        p[i][5] = p[i+1][5], k[i][5] = k[i+1][5];
        if( b[i] ) {
            int cost = F(H, h[i], d[i]);
            if( cost != 0x3f3f3f3f ) {
                if( p[i][5] > p[i+1][0] + 1 ) continue;
                if( p[i][5] == p[i+1][0] + 1 && k[i][5] <= k[i+1][0] + cost ) continue;
                p[i][5] = p[i+1][0] + 1;
                k[i][5] = k[i+1][0] + cost;
            }
        }
    }
    printf( "%d %d\n", p[1][0], k[1][0] );
}

int main()
{
    //ios_base::sync_with_stdio(false);
    while( scanf( "%d", &n ) != EOF ) {
        for( int i = 1; i <= n; ++i ) {
            scanf( "%d", b + i );
            if( b[i] ) scanf( "%d%d", h + i, d + i );
        }
        scanf( "%d%d%d", &H, &D, &P );
        solve();
    }
    return 0;
}


你可能感兴趣的:(ZOJ 3719 Diablo III 动态规划)