HDU 3646 DP + 二分
链接:http://acm.hdu.edu.cn/showproblem.php?pid=3646
题意:你有N把武器,每把武器可以对敌人造成一定的伤害(et:攻击力500,敌人血量为200,杀死敌人,攻击力剩余300),一共有K个敌人,你有M次魔法double武器的攻击力(加倍),使用武器是有规则的:武器有两个状态,一个状态为young,一个为old,新的武器状态为young,当你用它杀死一个敌人之后,状态变为old,当状态为young的时候,即使该武器剩余的攻击力不足以杀死当前的敌人,但是可以伤害他一定血量,但是为old的时候,武器攻击力不足以杀死对方,就无法攻击他(比如攻击力为200,敌人血量500,young:敌人减少200,old:敌人血量不减)。使用武器的顺序和杀人的顺序是给定的。问你最多能杀死几个人?
见解:如果没有old,young的话,这是一个水DP,有了这个状态的话,我们不好处理,主要是不知道当前武器的状态是什么,如果加一维表示当前状态的话,那么还得再来一维表示正在挑战第几个敌人,我们可以这样理解状态,dp【i】【j】表示使用完了i把武器,用了j次魔法,一共造成的伤害是多少,转移的时候,二分下现在的伤害就能知道现在在杀第a个人,然后二分再使用一把武器的造成的伤害,能知道挑战到第b个人,如果两个人不是同一个人,那说明我们无法伤害b,那么使用这把武器后我们能造成的伤害就是sum【b】了,最后二分伤害,就可以知道最多杀死几个人了,详细看代码:
#include<stdio.h> #include<string.h> #include<algorithm> #define Max(a,b) ((a)>(b)?(a):(b)) using namespace std; const int maxn = 105; const int maxm = 100005; int dp[2][maxn],life[maxm],power[maxn * maxn],N,M,K; int sum[maxm]; int main() { int cnt = 0; while(scanf("%d%d%d",&N,&M,&K) && N + M + K) { for(int i = 1;i <= N;i ++) scanf("%d",&power[i]); for(int i = 1;i <= K;i ++) { scanf("%d",&life[i]); sum[i] = sum[i - 1] + life[i]; } int ans = 0; M = min(M,N); memset(dp,0,sizeof(dp)); for(int i = 1;i <= N && !ans;i ++) { for(int j = 0;j <= M;j ++) { int a , b, next_a,next_b; a = upper_bound(sum + 1,sum + 1 + K,dp[(i - 1) & 1][j]) - sum;// now killed if(j) { b = upper_bound(sum + 1,sum + 1 + K,dp[(i - 1) & 1][j - 1]) - sum;//now killed j - 1 next_b = upper_bound(sum + 1,sum + 1 + K,dp[(i - 1) & 1][j - 1] + (power[i] << 1)) - sum;// use power } next_a = upper_bound(sum + 1,sum + 1 + K,dp[(i - 1) & 1][j] + power[i]) - sum;//not use power int hurt_a = dp[(i - 1) & 1][j] + power[i],hurt_b ; if(a != next_a) hurt_a = sum[next_a - 1]; if(j) hurt_b = dp[(i - 1) & 1][j - 1] + (power[i] << 1); if(j) if(b != next_b) hurt_b = sum[next_b - 1]; if(j)dp[i & 1][j] = Max(hurt_a,hurt_b); else dp[i & 1][j] = hurt_a; if(dp[i & 1][j] >= sum[K]) { ans = K; break; } } } if(!ans) ans = upper_bound(sum + 1,sum + 1 + K,dp[N & 1][M]) - sum - 1; printf("%d\n",ans); } return 0; }