HDU 4976 A simple greedy problem. DP

来源:互联网 发布:百度知道评论软件 编辑:程序博客网 时间:2024/04/30 04:33

【题目大意】

玩家A和玩家B在比赛,有n个小兵,每个小兵有一个初始血量hi。A先攻击,他可以选择最多一个小兵,对其造成一点伤害;然后B攻击,B会对所有小兵造成1点伤害。如果小兵的血量到0了,它就死了,造成最后一点伤害的玩家可以得1分。问,A最多得多少分。

【思路】

我们很容易想到一点,如果在一开始所有小兵的血量都不同,那么显然,A可以杀死所有小兵。那么,A的攻击,可以改成两种模式,造成最后一点伤害,或者垫刀,使得小兵的血量尽量不同。而且,我们也可以发现垫刀的优先级要小于得1分的优先级。现在问题的关键是,如果当前没有直接得分策略,我们具体怎么垫刀?最开始,我认为这里应该有一个贪心策略,但是。。。这里是没有贪心策略的。。。

如果我们把小兵按血量排序,假定B不攻击,依次计算,把所有小兵的血量都攻击到不同,每个小兵最终的血量是多少。注意,这里是一定要排序的,比方,如果小兵的血量分别是5 5 4 4 2 1

不排序(x表示不可能)

小兵的血量:5 5 4 4 2 1

垫刀至不同:5 4 3 2 1 x

花费: 0 1 1 1 1 ∞

排序

小兵的血量:5 5 4 4 2 1

垫刀至不同:5 x  4 3 2 1

花费: 0 ∞ 0 1 0 0

显然,排序之后的垫刀花费才是最优策略之一。有了上面的表,如果我们一定要杀死一个小兵,我们会发现,一个最优策略一定是在B攻击次数小于等于“垫刀至不同”的那个血量之前,把这个小兵打到这个血量,然后让B把它打成1血,补最后一刀。这个时候我们终于知道为什么贪心错的了,因为垫刀的优先级受到“花费”和“血量”两个因素影响,“花费”相同,究竟是垫高血量的兵的刀,还是垫低血量的兵,这里是不能说清楚的。

那么,我们可以使用DP解决这个问题,用dp[i][j],表示进行了i组攻击(A攻击一次,B攻击一次,称为一组),A留有j次攻击,最多能杀死的小兵数目。因为一个小兵的最优杀死策略是确定的(A先垫刀到“至不同”的那个血量,B攻击到1血,A补最后一刀),那么DP还是很容易做的。


//#pragma comment(linker, "/STACK:102400000,102400000")#include<cstdio>#include<cstring>#include<vector>#include<queue>#include<cmath>#include<cctype>#include<string>#include<algorithm>#include<iostream>#include<ctime>#include<map>#include<set>using namespace std;#define MP(x,y) make_pair((x),(y))#define PB(x) push_back(x)typedef __int64 LL;//typedef unsigned __int64 ULL;/* ****************** */const LL INF = 1LL<<55;const double INFF = 1e100;const double eps = 1e-8;const LL mod = 10000000007LL;const int NN = 1010;const int MM = 400010;/* ****************** */int re[NN], a[NN], dp[NN][NN];int main(){    int cas, ee = 0;    int i, j, t, n, max_n, ans;    scanf("%d", &cas);    while(cas--)    {        scanf("%d", &n);        memset(re, -1, sizeof(re));        max_n = 0;        for(i = 1; i <= n; i ++)        {            scanf("%d", &a[i]);            max_n = max(max_n, a[i]);        }        sort(a+1, a+1+n);        for(i = 1; i <= n; i ++)        {            for(j = a[i]; j >= 1; j --)            {                if(re[j] == -1)                {                    re[j] = a[i];                    break;                }            }        }        memset(dp, -1, sizeof(dp));        dp[0][0] = 0;        for(i = 0; i < max_n; i ++)            for(j = 0; j <= i + 1; j ++)                if(dp[i][j]!=-1)                {                    dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j]);                    t = j + 1 + i - re[i+1];                    if(re[i+1] != -1 && t >= 0)                    {                        dp[i+1][t] = max(dp[i+1][t], dp[i][j]+1);                    }                }        ans = 0;        for(j = 0; j <= max_n + 1; j ++)            ans = max(ans, dp[max_n][j]);        printf("Case #%d: %d\n", ++ee, ans);    }    return 0;}


0 0
原创粉丝点击