博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hdu4976 A simple greedy problem. (贪心+DP)
阅读量:6429 次
发布时间:2019-06-23

本文共 5524 字,大约阅读时间需要 18 分钟。

1006

A simple greedy problem.

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 20    Accepted Submission(s): 4
Problem Description
Victor and Dragon are playing DotA. Bored of normal games, Victor challenged Dragon with a competition of creep score (CS). In this competition, there are N enemy creeps for them. They hit the enemy one after another and Dragon takes his turn first. Victor uses a strong melee character so that in his turn, he will deal 1 damage to all creeps. Dragon uses a flexible ranged character and in his turn, he can choose at most one creep and deal 1 damage. If a creep take 1 damage, its health will reduce by 1. If a creep’s current health hits zero, it dies immediately and the one dealt that damage will get one score. Given the current health of each creep, Dragon wants to know the maximum CS he can get. Could you help him?
 
Input
The first line of input contains only one integer T(<=70), the number of test cases.
For each case, the first line contains 1 integer, N(<=1000), indicating the number of creeps. The next line contain N integers, representing the current health of each creep(<=1000).
 
Output
Each output should occupy one line. Each line should start with "Case #i: ", with i implying the case number. For each case, just output the maximum CS Dragon can get.
 
Sample Input
2 5 1 2 3 4 5 5 5 5 5 5 5
 
Sample Output
Case #1: 5 Case #2: 2
 
Source
 
Recommend
hujie   |   We have carefully selected several similar problems for you:          

题意:

有n个小兵,编号1~n,血量为ai,均为整数。现在两人轮流补兵,主角先选最多一个兵造成一点伤害(也就是可以不选),然后对手对所有兵造成1点伤害,循环。求主角最多能补多少个兵。兵数<=1000,兵血量<=1000。

 

题解:先贪心预处理出每个小兵需要主角垫多少刀,然后DP求得最优解。

我们先统计血量为x的小兵有d[x]个,最大血量为ma。这样,如果主角不补刀,第一回合对手会杀完所有1血小兵,第二回合2血,第三回合3血……主角为了补更多的兵,应该使小兵尽量分布在不同的血量。d[x]=0,说明x血量的小兵有0个,这样主角在这个血量的时候就补不到刀,我们需要利用若干个补不到刀的时机来对血量高的小兵进行垫刀,把它的血量削到没有小兵的血量的位置。

从1到最大血量ma扫一遍d[i],若当前d[i]为0,将其下标存入栈中;若大于1,则除了这个血量的时候补的这一刀,还可以将(d[i]-1)个小兵用之前的空闲时机垫刀垫到前面去。最优垫法就是把它垫到之前出现的最后一个d[i]为零的位置,就是我们用栈存的栈顶。

用一个c[i]来记录这个位置应该补的小兵原来在什么位置(主角在第i回合能补到的小兵,本来是多少血)。若d[i]不为0,则c[i]=i;若d[i]为0,按上面说的会在扫的过程中位置被存入栈中,之后d[j]>1的时候栈顶元素会c[i]=j,相当于在第i个回合能补到j血小兵,需要的垫刀次数是(c[i]-i)。

上面说的就是贪心预处理,代码如下:

1         int b[1111],bn=0; 2         mz(c); 3         int need; 4         for(i=1; i<=ma; i++) { 5             if(d[i]!=0) { 6                 while(d[i]>1) { 7                     if(bn>0) need=i-b[bn-1];///需要need步把它射到最后一个零处,然后还用b[r-1]这1步把它射死 8                     else break;///没有剩余的零就跳出 9                     //printf("i=%d,need=%d,r=%d,l=%d,r-l=%d,b[%d]=%d,b[%d]=%d\n",i,need,r,l,r-l,l+need-1,b[l+need-1],r-1,b[r-1]);10                     if(need+1<=bn) { ///空闲的步数足以把它射到最后一个空格(b[bn-1])处11                         c[b[bn-1]]=i;12                         bn--;///把这个零用掉13                         d[i]--;14                     } else break;///空闲步数不够就跳出15                 }16                 c[i]=i;///补这个怪17             } else {18                 b[bn++]=i;///记录零19             }20         }

 

然后我们就得到了c[i]这个数组,用它来DP一下就能得到答案。

f[i][j],为AOE杀i血怪前(也就是第i回合),我攻击结束后,我预留攻击还剩下j次,最多杀多少怪(包括i血怪)。

因为c[i]-i为杀这个怪所需的垫刀数,下面这个就是我补这个刀的状态转移。

f[i][j]=max(f[i][j] , f[i-1][j+c[i]-i]+1);

整个DP如下,还是挺简单的:

1         mz(f);2         for(i=1; i<=ma; i++) {3             for(j=0; j<=i; j++) {4                 if(j>0)f[i][j]=f[i-1][j-1];///这次不打,存一个攻击机会5                 if(c[i]!=0 && j+c[i]-i < i ) f[i][j]=max(f[i][j] , f[i-1][j+c[i]-i]+1);///怒把这个小兵打爆6             }7         }

 

全代码:

1 //#pragma comment(linker, "/STACK:102400000,102400000") 2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 using namespace std;13 #define ll long long14 #define usll unsigned ll15 #define mz(array) memset(array, 0, sizeof(array))16 #define minf(array) memset(array, 0x3f, sizeof(array))17 #define REP(i,n) for(i=0;i<(n);i++)18 #define FOR(i,x,n) for(i=(x);i<=(n);i++)19 #define RD(x) scanf("%d",&x)20 #define RD2(x,y) scanf("%d%d",&x,&y)21 #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)22 #define WN(x) prllf("%d\n",x);23 #define RE freopen("D.in","r",stdin)24 #define WE freopen("1biao.out","w",stdout)25 #define mp make_pair26 #define pb push_back27 int n;28 int d[1111];///d[i] i血怪有多少个29 int c[1111];///c[i] 在i位置应该补的怪本来在c[i]位置(本来是个c[i]血怪30 int f[1111][1111];///f[i][j],AOE杀i血怪前,我攻击结束后,我预留攻击还剩下j次,最多杀多少怪(包括i血怪)31 int main() {32 int t,n,i,j;33 int x;34 int cas=1;35 scanf("%d",&t);36 while(t--) {37 scanf("%d",&n);38 mz(d);39 int ma=0;40 REP(i,n) {41 scanf("%d",&x);42 d[x]++;43 ma=max(x,ma);44 }45 int b[1111],bn=0;46 mz(c);47 int need;48 for(i=1; i<=ma; i++) {49 if(d[i]!=0) {50 while(d[i]>1) {51 if(bn>0) need=i-b[bn-1];///需要need步把它射到最后一个零处,然后还用b[r-1]这1步把它射死52 else break;///没有剩余的零就跳出53 //printf("i=%d,need=%d,r=%d,l=%d,r-l=%d,b[%d]=%d,b[%d]=%d\n",i,need,r,l,r-l,l+need-1,b[l+need-1],r-1,b[r-1]);54 if(need+1<=bn) { ///空闲的步数足以把它射到最后一个空格(b[bn-1])处55 c[b[bn-1]]=i;56 bn--;///把这个零用掉57 d[i]--;58 } else break;///空闲步数不够就跳出59 }60 c[i]=i;///补这个怪61 } else {62 b[bn++]=i;///记录零63 }64 }65 // for(i=1;i<=ma;i++)66 // printf("%3d",c[i]);67 // puts("");68 mz(f);69 for(i=1; i<=ma; i++) {70 for(j=0; j<=i; j++) {71 if(j>0)f[i][j]=f[i-1][j-1];///这次不打,存一个攻击机会72 if(c[i]!=0 && j+c[i]-i < i ) f[i][j]=max(f[i][j] , f[i-1][j+c[i]-i]+1);///怒把这个小兵打爆73 }74 }75 int ans=0;76 for(j=0; j
View Code

 

(比赛时第二个看这个题,因为他题目上写着贪心嘛,看起来很水的样子!我也有点思路,写了个贪心,每次把怪垫到之前最后的零的位置,完全没想到这样不是最优……居然还要DP才行,怕了,被题目骗了!)

转载于:https://www.cnblogs.com/yuiffy/p/3927941.html

你可能感兴趣的文章
bzoj1593[Usaco2008 Feb]Hotel 旅馆*
查看>>
SQL语句中DateAdd 函数说明
查看>>
柔性数组
查看>>
WPF个人助手更新
查看>>
NLPIR技术助力中文智能数据挖掘
查看>>
python操作redis--------------数据库增删改查
查看>>
Android中仿IOS提示框的实现
查看>>
php初学第一课
查看>>
Windows下与Linux下编写socket程序的区别 《转载》
查看>>
java学习笔记 --- IO(3)
查看>>
buntu的ip设置
查看>>
Mysql 的FIND_IN_SET函数慢的忧化
查看>>
Web service是什么?
查看>>
Could not resolve placeholder
查看>>
怎么读书?
查看>>
手动删除data里面的mysql-bin文件后无法启动数据库
查看>>
Python基础学习五 内置函数
查看>>
移动端相关事件
查看>>
【重学计算机】操作系统D6章:并发程序设计
查看>>
(转)AE加载不同数据的方法(GeoDatabase空间数据管理)
查看>>