team2012-F3-sol-0020
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
题意:
游戏有n个关卡,有几个关卡有BOSS,只能在别的普通关卡拿到5个BUFF以后才能打
打的时候是一个回合制形式,给定BOSS和英雄的攻击力和血量,且英雄可以喝血瓶回复,问最多能打几个BOSS获得宝物,在这情况下最少用几个血瓶。
解法:
对于每个BOSS战的时候可以用DP预处理出 f[BOSS血量][英雄血量] 的时候需要用多少血瓶,转移的时候分三种
1,双方各砍一刀不喝血瓶
2,在BOSS砍英雄前喝
3,在BOSS砍英雄后喝
要注意英雄血量不能超过限度,且要保证英雄不死
然后就可以进行主DP,设 f[i]为一个pair 表示打到第i个BOSS关能得多少宝物和用几个血瓶,那么可以有两种选择
1,不打这个BOSS,由 f[i-1] 得到
2,打这个BOSS,由前面最近的相隔5个或以上普通关卡的BOSS关 f[j] 得到
第二种的实现可以用一个deque,每次从队首出队一个j,看j能不能转移到i,能则继续出队直到不能为止,那么最后一个j就是最近的,每次处理完后将i入队尾,是线性复杂度。
}}}
{{{
#include <iostream>
#include <cstdio>
#include <deque>
#include <cstring>
using namespace std;
template<class T> bool takemin(T& a, T b) {bool Z=(a>b); if(Z)a=b; return Z;}
template<class T> bool takemax(T& a, T b) {bool Z=(a<b); if(Z)a=b; return Z;}
const int MAXN = 50005, MAXHP = 1003, MAXATK = 11, INFI = 2100000000;
int lst[MAXN], hp[MAXN], dm[MAXN], f[MAXN], cost[MAXN];
int m_hp, m_dm, rec, dp[MAXHP][MAXHP][MAXATK];
void init()
{
for(int a = 1; a < MAXATK; a++)
for(int i = 0; i < MAXHP; i++)
for(int j = 0; j <= m_hp; j++)
{
int& ans = dp[i][j][a] = INFI;
if(i == 0) { ans = 0; continue; }
if(j == 0) { ans = INFI; continue; }
int hp2 = max(i - m_dm, 0);
if(hp2 == 0) { ans = 0; continue; }
//not recover
int m_hp2 = j - a;
if(m_hp2 > 0) takemin(ans, dp[hp2][m_hp2][a]);
//recover before boss attack
m_hp2 = min(j + rec, m_hp);
m_hp2 -= a;
if(m_hp2 > 0) takemin(ans, dp[hp2][m_hp2][a] + 1);
//recover after boss attack
m_hp2 = j - a;
if(m_hp2 > 0)
{
m_hp2 = min(m_hp2 + rec, m_hp);
takemin(ans, dp[hp2][m_hp2][a] + 1);
}
}
}
int main()
{
int n;
while(~scanf("%d", &n))
{
int tot = 0, tp;
for(int i=1;i<=n;i++)
{
scanf("%d", &tp);
if(tp)
{
lst[++tot] = i;
scanf("%d %d", &hp[tot], &dm[tot]);
}
}
scanf("%d %d %d", &m_hp, &m_dm, &rec);
init();
f[0] = cost[0] = 0;
deque<int> q;
q.push_back(0);
lst[0] = 0;
for(int i=1;i<=tot;i++)
{
int ok = 0, id;
while(!q.empty() && lst[i] - lst[q.front()] - q.size() >= 5)
{
ok = 1;
id = q.front();
q.pop_front();
}
f[i] = f[i-1];
cost[i] = cost[i-1];
if(ok)
{
q.push_front(id);
int tmp = dp[hp[i]][m_hp][dm[i]];
if(tmp != INFI)
{
if(takemax(f[i], f[id] + 1))
cost[i] = cost[id] + tmp;
else if(f[i] == f[id] + 1)
takemin(cost[i], cost[id] + tmp);
}
}
q.push_back(i);
}
printf("%d %d\n", f[tot], cost[tot]);
}
return 0;
}
}}}
题意:
游戏有n个关卡,有几个关卡有BOSS,只能在别的普通关卡拿到5个BUFF以后才能打
打的时候是一个回合制形式,给定BOSS和英雄的攻击力和血量,且英雄可以喝血瓶回复,问最多能打几个BOSS获得宝物,在这情况下最少用几个血瓶。
解法:
对于每个BOSS战的时候可以用DP预处理出 f[BOSS血量][英雄血量] 的时候需要用多少血瓶,转移的时候分三种
1,双方各砍一刀不喝血瓶
2,在BOSS砍英雄前喝
3,在BOSS砍英雄后喝
要注意英雄血量不能超过限度,且要保证英雄不死
然后就可以进行主DP,设 f[i]为一个pair 表示打到第i个BOSS关能得多少宝物和用几个血瓶,那么可以有两种选择
1,不打这个BOSS,由 f[i-1] 得到
2,打这个BOSS,由前面最近的相隔5个或以上普通关卡的BOSS关 f[j] 得到
第二种的实现可以用一个deque,每次从队首出队一个j,看j能不能转移到i,能则继续出队直到不能为止,那么最后一个j就是最近的,每次处理完后将i入队尾,是线性复杂度。
#include <iostream>
#include <cstdio>
#include <deque>
#include <cstring>
using namespace std;
template<class T> bool takemin(T& a, T b) {bool Z=(a>b); if(Z)a=b; return Z;}
template<class T> bool takemax(T& a, T b) {bool Z=(a<b); if(Z)a=b; return Z;}
const int MAXN = 50005, MAXHP = 1003, MAXATK = 11, INFI = 2100000000;
int lst[MAXN], hp[MAXN], dm[MAXN], f[MAXN], cost[MAXN];
int m_hp, m_dm, rec, dp[MAXHP][MAXHP][MAXATK];
void init()
{
for(int a = 1; a < MAXATK; a++)
for(int i = 0; i < MAXHP; i++)
for(int j = 0; j <= m_hp; j++)
{
int& ans = dp[i][j][a] = INFI;
if(i == 0) { ans = 0; continue; }
if(j == 0) { ans = INFI; continue; }
int hp2 = max(i - m_dm, 0);
if(hp2 == 0) { ans = 0; continue; }
//not recover
int m_hp2 = j - a;
if(m_hp2 > 0) takemin(ans, dp[hp2][m_hp2][a]);
//recover before boss attack
m_hp2 = min(j + rec, m_hp);
m_hp2 -= a;
if(m_hp2 > 0) takemin(ans, dp[hp2][m_hp2][a] + 1);
//recover after boss attack
m_hp2 = j - a;
if(m_hp2 > 0)
{
m_hp2 = min(m_hp2 + rec, m_hp);
takemin(ans, dp[hp2][m_hp2][a] + 1);
}
}
}
int main()
{
int n;
while(~scanf("%d", &n))
{
int tot = 0, tp;
for(int i=1;i<=n;i++)
{
scanf("%d", &tp);
if(tp)
{
lst[++tot] = i;
scanf("%d %d", &hp[tot], &dm[tot]);
}
}
scanf("%d %d %d", &m_hp, &m_dm, &rec);
init();
f[0] = cost[0] = 0;
deque<int> q;
q.push_back(0);
lst[0] = 0;
for(int i=1;i<=tot;i++)
{
int ok = 0, id;
while(!q.empty() && lst[i] - lst[q.front()] - q.size() >= 5)
{
ok = 1;
id = q.front();
q.pop_front();
}
f[i] = f[i-1];
cost[i] = cost[i-1];
if(ok)
{
q.push_front(id);
int tmp = dp[hp[i]][m_hp][dm[i]];
if(tmp != INFI)
{
if(takemax(f[i], f[id] + 1))
cost[i] = cost[id] + tmp;
else if(f[i] == f[id] + 1)
takemin(cost[i], cost[id] + tmp);
}
}
q.push_back(i);
}
printf("%d %d\n", f[tot], cost[tot]);
}
return 0;
}