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