比赛三个多小时一直在写1006,不停WA,回来照着原来思路重写之后一下就A掉了,很无语。
这道题如果不能使用双倍攻击,那就是道贪心大水题,加了可以使用M次双倍以后就是DP。
dp[i][j]表示前i个火鸟使用j次双倍攻击,那么应该保存两个值,一个是当前已杀怪次数cnt,另一个是当前面对的怪剩下的生命值。
dp[i][j]可以由dp[i-1][j]或dp[i-1][j-1]转移而来,双倍时贪心选择在火鸟攻击初用,杀怪过程同样贪心。
代码如下:
#include<iostream> using namespace std; const int inf = 2000000000; const int maxn = 10010; const int maxk = 100010; const int maxm = 110; struct nod_t { int cnt, rem; }; int N, M, K; int f[maxn], k[maxk]; nod_t dp[maxn][maxm]; //dp[i][j]表示前i个火鸟使用j次double后, //杀死怪物的最多次数cnt,和正要杀的怪物的剩下的生命rem; nod_t Get0(int i, int j)//计算dp[i-1][j]状态的下一个状态,输入保证i<j { nod_t nod0; if(dp[i-1][j].rem==inf) //这时,dp[i-1][j]的cnt已经等于K了. { nod0.cnt = dp[i-1][j].cnt; nod0.rem = inf; } else { if(f[i] < dp[i-1][j].rem) //当前火鸟f[i]太小,杀不过当前要杀的怪 { nod0.cnt = dp[i-1][j].cnt; nod0.rem = dp[i-1][j].rem - f[i]; //那么就拼掉f[i]的生命 } else if(f[i] == dp[i-1][j].rem) //1vs1拼掉 { int killed = dp[i-1][j].cnt; nod0.cnt = killed + 1; nod0.rem = k[killed+1]; } else //当前火鸟可以杀至少一个怪. { int life = f[i]; int killed = dp[i-1][j].cnt; life -= dp[i-1][j].rem; killed++; while(life >= k[killed+1]) //能够杀死下一个怪 { killed++; life -= k[killed]; if(life==0) break; //如果当前生命为0,那么火鸟死亡 } nod0.cnt = killed; nod0.rem = k[killed+1]; } } return nod0; } nod_t Get1(int i, int j) //计算dp[i-1][j-1]状态的下一个状态,输入保证j>0 { nod_t nod1; if(dp[i-1][j-1].rem==inf) { nod1.cnt = dp[i-1][j-1].cnt; nod1.rem = inf; } else { if(2*f[i] < dp[i-1][j-1].rem) { nod1.cnt = dp[i-1][j-1].cnt; nod1.rem = dp[i-1][j-1].rem - 2*f[i]; } else if(2*f[i] == dp[i-1][j-1].rem) { int killed = dp[i-1][j-1].cnt; killed++; nod1.cnt = killed; nod1.rem = k[killed+1]; } else { int life = 2*f[i]; int killed = dp[i-1][j-1].cnt; life -= dp[i-1][j-1].rem; killed++; //至少能杀死一个怪 while(life >= k[killed+1]) //能够杀死下一个怪 { killed++; life -= k[killed]; if(life==0) break; //如果当前生命为0,那么火鸟死亡 } nod1.cnt = killed; nod1.rem = k[killed+1]; } } return nod1; } int main() { int i, j; while(scanf("%d %d %d", &N, &M, &K)==3) { if(N==0&&M==0&&K==0) break; for(i = 1; i <= N; i++) { scanf("%d", &f[i]); } for(i = 1; i <= K; i++) { scanf("%d", &k[i]); } k[K+1] = inf; //结束标记 if(M>N) M = N; //trick memset(dp, 0, sizeof(dp)); dp[0][0].cnt = 0; dp[0][0].rem = k[1]; for(i = 1; i <= N; i++) { for(j = 0; j <= M && j <= i; j++) { nod_t nod0, nod1; //nod0,nod1分别是由dp[i-1][j],dp[i-1][j-1]转换来的状态 if(i!=j) //nod0在i<j的情况下合法 { nod0 = Get0(i, j); } if(j!=0) // j>0合法 { nod1 = Get1(i, j); } if(j==0) dp[i][j] = nod0; //只能为nod0 else if(i==j) dp[i][j] = nod1; //同上 else { //第一优先选杀怪数多的,第二优先下个怪生命少的, if(nod0.cnt > nod1.cnt) dp[i][j] = nod0; else if(nod0.cnt < nod1.cnt) dp[i][j] = nod1; else { if(nod0.rem < nod1.rem) dp[i][j] = nod0; else dp[i][j] = nod1; } } }//for j }// for i // 要在前面加上if(M>N) M=N,因为数据中有M>N的情况 printf("%d/n", dp[N][M].cnt); /* int ans = 0; for(j = 0; j <= M && j <= N; j++) { if(dp[N][j].cnt > ans) ans = dp[N][j].cnt; } printf("%d/n", ans); */ } return 0; }