HDU 2809 God of War(DP + 状态压缩)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2809

题目大意:给出战神吕布的初始攻击力ATI、防御力DEF、生命值HP、每升一级增加的攻击力In_ATI,增加的防御力In_DEF和增加的生命值In_HP。然后给出n个敌人的攻击力、防御力、生命值和杀死该单位能获得的经验值EXP。  吕布的初始经验值EXP是0,初始等级level是1,每当EXP>=level*100时就会升级。

  在吕布LvBu和敌人A之间的战斗有3条规则

  1.吕布攻击A,A失去 Max(1,LvBu's ATI- A's DEF) HP;

  2.A攻击吕布,吕布失去Max(1,A'ATI- LvBu'DEF)HP;

  3.战斗持续到任意一方死亡

如果吕布能够将全部敌人杀死,并且自己不死,那么输出能够余下的最大HP,否则输出"Poor LvBu,his period was gone."

Sample Input
100 80 100 5 5 5
2
ZhangFei 95 75 100 100
XuChu 90 90 100 90
100 75 100 5 5 5
1
GuanYu 95 85 100 100
 
Sample Output
30
Poor LvBu,his period was gone.

分析:

由于涉及到最优结果 以及 单挑顺序对结果带来的影响,使用状态压缩dp来解这道题。

想讲一下思路,比如现在吕布跟三个人战斗,用1表示已经战胜这个人, 0表示没有。那个所有的情况(在不考虑顺序的情况下)就有2的3次方,8种可能,包括000。用对应的十进制表示。如图

hdu <wbr>2809 <wbr>God <wbr>of <wbr>War

  可见,3的状态可以由1,2来求,5可以用1,4来求,6可以用2, 4来求,7可以用3, 5, 6状态来求,这就有了一个状态转移的方程在里面了。

有些同学就想问那顺序怎么办呢。

  我们要计算3的最优状态的时候,会由1,跟2,来推出

  1状态表示打败第一个人

  2状态只表示第二个人

  那么3状态由1状态来计算的时候,就是先打第一个人再打第二个人

  而3状态由2状态来计算的时候,就是先打第二个人再打第一个人, 计算后的结果与当前的3状态的结果比较,把最优的覆盖上去就可以了。

  这个过程是不是有了一个顺序在里面了呢。

 

  而在代码里面,我们是由底向上计算额。比如历遍到第1个状态点的。我们可以计算上面的第3个点,跟第5个状态点,寻找的过程由位运算来实现。

代码如下:

 1 # include<stdio.h>
 2 # include<string.h>
 3 int max(int a,int b){
 4     return a>b ?a :b;
 5 }
 6 struct Lvbu{
 7     int ATI,DEF,HP,EXP,level;
 8 }lv,dp[1<<20];    //dp[i]表示在在集合i下,吕布能达到的最好状态
 9 
10 struct Enemy{
11     char name[25];
12     int ATI,DEF,HP,EXP;    
13 }en[20];
14 
15 int In_ATI,In_DEF,In_HP,n;
16 
17 int main(){
18     int i,j;
19     while(scanf("%d%d%d%d%d%d",&lv.ATI,&lv.DEF,&lv.HP,&In_ATI,&In_DEF,&In_HP) != EOF){
20         lv.EXP = 0;        //吕布初始经验为0,初始等级为1
21         lv.level = 1;
22         scanf("%d",&n);
23         for(i=0;i<n;i++)
24             scanf("%s%d%d%d%d",en[i].name,&en[i].ATI,&en[i].DEF,&en[i].HP,&en[i].EXP);
25         int s,recent;
26         int endstate = 1<<n;
27         dp[0] = lv;    //dp[0]表示集合0,即没有攻击任何一个敌人时,吕布的状态,初始化
28         bool flag;
29         
30         for(s=1; s<endstate; s++){
31             flag = false;    //判断是否可以打败敌人
32             for(i=0; i<n; i++){
33                 recent = 1<<i;        
34                 if(s & recent){        //如果在该集合里边,吕布打败了第i个敌人
35                     Lvbu tmp=dp[s^recent];        //取出吕布没有打败第i个人,但是打败了状态s里边的其他所有人
36                     if(tmp.level < 1)    continue;    //已经死亡
37                     int dec = tmp.ATI - en[i].DEF;    //吕布的攻击力
38                     if(dec <= 0) dec = 1;        //特殊情况
39                     
40                     int times = en[i].HP / dec;        //吕布杀死敌人的时间
41                     if(times * dec != en[i].HP) times++;
42                     
43                     dec = en[i].ATI - tmp.DEF;    //敌人的攻击力
44                     if(dec <= 0) dec = 1;
45                     tmp.HP -= dec*(times - 1);    //吕布受到的伤害
46                     
47                     if(tmp.HP <= 0)        continue;    //如果死亡,跳出
48                     
49                     tmp.EXP += en[i].EXP;    //获得该角色经验
50                     if(tmp.EXP >= tmp.level* 100){    //如果经验足够升级
51                         int add = tmp.EXP/100 - tmp.level + 1;        //可升多少级
52                         tmp.level += add;        //各项技能强化
53                         tmp.ATI += add*In_ATI;
54                         tmp.DEF += add*In_DEF;
55                         tmp.HP += add*In_HP;
56                     }
57                     if(!flag || (tmp.HP>dp[s].HP) ||tmp.level>dp[s].level){        //选取最优的方案,并且置flag为真
58                         dp[s] = tmp;
59                         flag = true;
60                     }
61                 }
62             }        
63             if(!flag) dp[s].level = -1;    //如果不能打败该集合里的敌人,那么令等级为-1
64         }
65         
66         lv = dp[endstate -1];    //将所有敌人打倒的状态 (1<<n)-1
67         if(lv.level < 1) 
68             printf("Poor LvBu,his period was gone.
");
69         else
70             printf("%d
",lv.HP);
71     }
72     return 0;
73 }
原文地址:https://www.cnblogs.com/acm-bingzi/p/3292512.html