状态压缩DP的一道较不错的入门题,第二次做这类问题,感觉不是很顺手,故记录下来.
题目的意思就是吕布战群雄,先给你6个数,分别是吕布的攻击值,防御值,生命值,升级后此三值各自的增量,然后是对手的个数num,接下来是各个对手的名字,攻击值,防御值,生命值,及经验值,规定当吕布经验值每提升100,则攻击值,防御值,生命值加上各自的增量。最后若生命值大于0,输出生命值,否则输出一句话"Poor LvBu,his period was gone."。
刚开始看到此题实在看不出是用压缩DP,以为是普通的DP。但只细一想,状态数太多了并且和编号有关联,原因是:假设与吕布对战的人有三个分别是关羽,张飞,许褚,则现在问题出来了。吕布按什么顺序来对战此三人,吕布-->关羽-->张飞-->许褚,吕布-->张飞-->关羽-->许褚等等,以及战胜一人后生命值是否为零,两人呢.......?这样想着发现很乱很乱,状态多并且不好表示(这不二进制中1正好是状态压缩擅长的嘛)。于是定义状态表达式LvBu[i],i对应的二进制中1代表已和此人对战,如5=101表示分别和关羽,许褚fight了,就差张飞了。但麻烦又来了,101是先和关羽打呢,还是先和许褚打,这里要特别注意的是和谁先打的顺序可能会导致结果不同,例如我先打关羽得到的经验值少不能升级,而我先打许褚升级的经验值就足够了,很明显这两个结果的生命值肯定不会相同。那么怎么解决呢?这只要在for循环时进行更新就可以了
接下来详细解释代码,
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 struct Person{ 7 int attck; 8 int defense; 9 int hp; 10 int expirence; 11 }LvBu[1<<21],Enemy[21],Update;//定义结构体,并分配相应的数组,对手最多20个 12 int result,num; 13 14 inline int max(int a,int b) //最大值函数 15 { 16 if(a>b) return a; 17 return b; 18 } 19 20 void Fight() 21 { 22 int attck,defence; 23 int myTime,Time; 24 int rHp,expirence,rattack,rdef; 25 for(int i=0;i<=result;i++)//状态 26 for(int j=0;j<num;j++)//对手编号从0到num-1 27 { 28 if(!(i&(1<<j))&&LvBu[i].hp>0)//若吕布未与编号j对手交战并且生命值不为零,有对战的必要 29 { 30 attck=max(1,LvBu[i].attck-Enemy[j].defense);//依题目条件吕布攻击一次对方掉attck点血 31 defence=max(1,Enemy[j].attck-LvBu[i].defense);//对方攻击一次吕布掉defence点血 32 myTime=(LvBu[i].hp+defence-1)/defence;//吕布可以攻击的总次数 33 Time=(Enemy[j].hp+attck-1)/attck;//对手可以攻击的总次数 34 //如关羽生命值是8,吕布攻击一次造成伤害为-3,那么吕布总共可以攻击3次,那么用一行代码如何表示呢?可以先拿1单位的血出来,剩下的直接除以伤害值 35 if(myTime<Time) continue;//吕布先挂了,否则继续执行 36 rHp=(LvBu[i].hp-(Time-1)*defence);//剩余生命值原本的减去代价值,Time减一的原因是对手先倒下了,原本你打我一下,我打你一下成偶数的,你走的早就打不到我了 37 expirence=Enemy[j].expirence+LvBu[i].expirence;//得到经验值 38 rattack=LvBu[i].attck;rdef=LvBu[i].defense;//之所以这样是方便后面书写 39 40 if(expirence>=100)//可以升级 41 { 42 expirence-=100;//付出100点经验值为代价 43 rattack+=Update.attck; 44 rdef+=Update.defense; 45 rHp+=Update.hp; 46 } 47 48 if(rHp>=LvBu[i|(1<<j)].hp)//这就是我先打许褚-->关羽,还是关羽-->许褚的更新了 49 { 50 LvBu[i|(1<<j)].attck=rattack; 51 LvBu[i|(1<<j)].defense=rdef; 52 LvBu[i|(1<<j)].hp=rHp; 53 LvBu[i|(1<<j)].expirence=expirence; 54 } 55 } 56 } 57 if( LvBu[result].hp) printf("%d\n",LvBu[result].hp); 58 else printf("Poor LvBu,his period was gone.\n"); 59 } 60 int main() 61 { 62 while(scanf("%d %d %d %d %d %d",&LvBu[0].attck,&LvBu[0].defense,&LvBu[0].hp,&Update.attck,&Update.defense,&Update.hp)!=EOF) 63 { //输入 64 LvBu[0].expirence=0;//吕布初始经验值为0 65 char name[21]; 66 scanf("%d",&num); 67 for(int i=0;i<num;i++) 68 scanf("%s%d%d%d%d",name,&Enemy[i].attck,&Enemy[i].defense,&Enemy[i].hp,&Enemy[i].expirence); 69 result=(1<<num)-1;//状态总数 70 71 for(int i=1;i<=result;i++) LvBu[i].hp=0;//初始化 72 Fight();//对战 73 } 74 return 0; 75 }
以后看着复习就省事多了
状态压缩DP----HDU2809
时间: 2024-10-29 02:48:02