http://acm.hdu.edu.cn/showproblem.php?pid=4976
A simple greedy problem.
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 219 Accepted Submission(s): 109
Problem Description
![](https://i-blog.csdnimg.cn/blog_migrate/9acd49d5c82fff66ee6e4ec8edb54c08.jpeg)
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).
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
Author
BJTU
Source
这题想了半天, 看题解说是DP, 想了半天过掉了... PS. 记忆化搜索比递推慢一倍, 结果在4976里交两个都能过, 前者812MS,后者484MS, 差别很明显,但都能过. 没想到在我们的WEB CONTEST训练里交了个记忆化搜索TLE了, 交递推则是花了562MS, 这是什么情况..
思路: f[i][j]表示只考虑前j个怪, 第i轮砍杀完, 所能杀掉的最多怪. 每个怪除去一个血值x(表示只能在前x轮杀掉), 还有一个需要补的刀数y(表示为了不跟其它怪的x值冲突,需要先给它y刀. 所以可以叫做花费).
记忆化搜索: if(i>=y && i<=x) return f[i][j] = max(search(i,j-1),1+search(i-y,j-1)); //分别向不砍他或者砍他搜下去.
递推: f[i][j] = max(f[i-1][j],f[i][j-1],1+f[i-v[j-1].Y][j-1]); //注意同样需要有 "if(i>=y && i<=x)" 的判断条件,同时需要注意循环顺序
思考过程: 搜这道题名称时就看到了大大的DP二字, 不得不向这方面想了.. 主角可以打也可以不打, 但每一轮所有怪都会掉1滴血. 显然只要所有怪血量都不相同, 就都可以杀死.//以上瞟自题解
开始想把那个血量上升子序列提取出来再处理剩下的重复了的血量, 没有思路, 但想到了每只怪的补刀次数y, 也就是每只怪需要的花费. 显然那些血量各不相同的怪的花费只是1,只要在它们血量的那一轮给它一刀就能杀死; 那些重复的血量, 除了其中一个花费是1, 其它的都需要向下找更小的还没有被占用的血量, 比如3,3,4,需要给其中一个3一刀让它降到2,然后再在第二轮一刀砍死. 也就是说这三个怪的花费是1,2,1. 但如果是3,4,4, 需要给其中一个4补两刀到2,但是补完两刀,第二轮末尾让对手一刀砍死了. 花费分别是1,1,3. (所以需要满足 y<=i<=x-y 么?? 没想清楚..)
后来发现分开处理麻烦, 想一起处理就想到了思路: 处理前j个怪到第i轮所能杀掉的最大数量,很明显是可以通过讨论第j个怪杀与不杀的两种情况转移过来. 第i轮相当于前面最多可以攒够i刀去补刀,所以只要i值大于第j个怪的花费,同时小于它的血量,就可以从之前转移过来.(这里似乎有错误...)
#include <iostream> #include <iomanip> #include <fstream> #include <sstream> #include <cassert> #include <algorithm> #include <string> #include <vector> #include <set> #include <map> #include <queue> #include <stack> #include <cmath> #include <numeric> #include <bitset> #include <cstdio> #include <cstring> using namespace std; #define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++) #define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++) #define X first #define Y second template<class T> inline void smin(T &a, T b){if(b<a)a=b;} template<class T> inline void smax(T &a, T b){if(a<b)a=b;} typedef long long ll; typedef long double ld; typedef pair<int, int> pii; template<class T> inline T min(T a, T b, T c){return min(min(a, b), c);} template<class T> inline T max(T a, T b, T c){return max(max(a, b), c);} template<class T> inline T min(T a, T b, T c, T d){return min(min(a, b), min(c, d));} template<class T> inline T max(T a, T b, T c, T d){return max(max(a, b), max(c, d));} template<class T> inline T sqr(T a){return a*a;} template<class T> inline T cub(T a){return a*a*a;} //// int t,n,tmp,mx; vector<pii> v; stack<int> s; int num[1009]; int f[1009][1009]; int search(int m,int k){//记忆化搜索, 外面交过了,里面居然挂了 if(m<=0 || k<=0) return 0; if(f[m][k]!=-1) return f[m][k]; if(m>=1 && v[k-1].Y==1 && m<=v[k-1].X) return f[m][k]=1+search(m-1,k-1); if(m>=v[k-1].Y && m<=v[k-1].X) return f[m][k] = max(search(m,k-1),1+search(m-v[k-1].Y,k-1)); return f[m][k] = max(search(m,k-1),search(m-1,k)); } void work(){//递推 fer(j,1,v.size()+1){ fer(i,1,mx+1){ f[i][j] = max(f[i-1][j],f[i][j-1]); if(i>=v[j-1].Y && i<=v[j-1].X-v[j-1].Y+1) smax(f[i][j],1+f[i-v[j-1].Y][j-1]); } } printf("%d\n",f[mx][v.size()] ); } int main() { freopen("in.txt","r",stdin); ios_base::sync_with_stdio(0); scanf("%d",&t); fer(tt,1,t+1){ printf("Case #%d: ",tt ); scanf("%d",&n); v.clear(); while(!s.empty()) s.pop(); memset(num,0,sizeof(num)); memset(f,-1,sizeof(f)); mx=-1; rep(i,n) scanf("%d",&tmp),smax(mx,tmp),num[tmp]++; fer(i,1,1001){ s.push(i); rep(j,num[i]){ if(s.empty()) break; tmp = s.top(); s.pop(); v.pb(mkp(i,i-tmp+1)); } } fer(i,1,1001) f[0][i]=f[i][0]=0; f[0][0]=0; work(); //printf("%d\n",search(mx,v.size()) ); } return 0; }