状态压缩DP----HDU2809

状态压缩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-08-30 00:59:44

状态压缩DP----HDU2809的相关文章

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

HDU1565(状态压缩dp)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8170    Accepted Submission(s): 3095 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数

HDU 3001【状态压缩DP】

题意: 给n个点m条无向边. 要求每个点最多走两次,要访问所有的点给出要求路线中边的权值总和最小. 思路: 三进制状态压缩DP,0代表走了0次,1,2类推. 第一次弄三进制状态压缩DP,感觉重点是对数据的预处理,利用数组分解各个位数,从而达到类似二进制的目的. 然后就是状态的表示,dp[s][i]表示状态s时到达i的最优值. 状态转移也一目了然,不废话. #include<stdio.h> #include<string.h> #include<algorithm> u

Victor and World(spfa+状态压缩dp)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5418 Victor and World Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)Total Submission(s): 958    Accepted Submission(s): 431 Problem Description After trying hard fo

poj 3311 Hie with the Pie(状态压缩dp)

Description The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait for 1 or more (up to 10) orders to be

HDU--1074(状态压缩DP)

典型的状态压缩DP,给出了每件作业的截止时间和花费,求让老师扣分最少的写作业方式.把完成n种作业用状态2^n-1表示,dp[s]表示 完成状态s时,最小扣分.比如“111”,那么可以由“011”,“110”,“101”转移过来,分别表示选了0,1号作业,1,2号作业,0,2号作业. t[s]表示状态S记录的总时间.dp[s] = min{dp[j]+c[k] - d[k]},其中j = i^(1<<k),0<k<n;pre[s]表示状态s完成时,最末尾完成的作业, #include

poj 3254 Corn Fields(状态压缩dp)

Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and

poj 2411 Mondriaan&#39;s Dream(状态压缩+dp)

 题意:用1*2砖块铺满n*m的房间. 思路转自:http://www.cnblogs.com/scau20110726/archive/2013/03/14/2960448.html 因为这道题输入范围在11*11之间,所以可以先打表直接输出.......... 状态压缩DP 经典覆盖问题,输入n和m表示一个n*m的矩形,用1*2的方块进行覆盖,不能重叠,不能越出矩形边界,问完全覆盖完整个矩形有多少种不同的方案 其中n和m均为奇数的话,矩形面积就是奇数,可知是不可能完全覆盖的.接着我们来看

1252 - Twenty Questions(状态压缩DP)

经典的状态压缩DP .  有没有感觉这道题和什么东西有点像?  没错,是01背包 . 将特征看作物品 , 只不过这里的状态有点复杂, 需要用一个集合才能表示它, 所以我们用d[s][a]来表示,已经询问了特征集s , 假设我们要猜的物品是w ,w所具备的特征集为a ,此时还要询问的最小次数 .   显然a是s的子集,而且要注意本题的要求, 求的是最小化的最大询问次数 .也就是说无论猜哪个物品,猜这么多次一定能猜到 . 那么状态如何转移呢? 就像背包问题,对于一个特征k ,我们要抉择:要k还是不要

hoj 2662 状态压缩dp

题意: 给定mxn的棋盘,要往其中放d枚棋子,其中,一枚棋子的上下左右四个位置不能再放棋子.求所有合法的放置状态 ( 0<m,n<=80 , mxn<=80 ) 棋盘位置可多达80,爆搜目测超时,我用的状态压缩dp. 棋盘中的一个格子,放棋用1,不放用0 压缩其中一维,每个状态数用一个二进制数表示. 每个状态都是棋盘一行的放置方法 假设,m >= n , 这个递推棋盘就有 m行 ,每行至多有 2^n-1个状态数.其实,没有这么多!按照题意,每个1的上下左右不能有1.对于一行,只知左