2002-2003 ACM-ICPC Northeastern European Regional Contest (NEERC 02) H Heroes Of Might And Magic (隐含dp)
问题是求一个方案,实际隐含一个dp。法力是递减的,所以状态是DAG,对于一个确定的状态,我们贪心地希望英雄的血量尽量大。
分析:定义状态dp[i][p][h]表示是已经用了i的法力值,怪兽的位置在p,怪兽的总血量为h时候英雄所具有的最大血量,
采用刷表法,决策有:
使用雷击,h变成h-L[p],p变成max(p-V,1),如果怪兽移动结束以后在1号位置且没有击杀怪物h-L[p]>0(注意这种情况),
会对英雄造成ceil((h-L[p])/HPm)点伤害,如果dp[i][p][h]-damge<=0,那么转移是非法的。
使用传送,传送到1是没有意义的,从2开始枚举,假设传送的位置为tp,那么怪物的下一个位置会是max(tp-V,1),处理一下伤害和转移即可。
使用治疗,注意判断一下血量上限。
具体处理的时候,记录一个pre状态数组和相应操作,用一个vis数组表示下一个阶段的状态是否出现过。
习惯用滚动数组,直接写反而觉得别扭。。。
转移麻烦的写个updata效果拔群
#include<bits/stdc++.h> using namespace std; const int maxn = 11; int N,HPh,MPh,HPm,Nm,V,dH; int L[maxn]; #define MP make_pair typedef pair<int,int> Node; #define fi first #define se second #define PB push_back const int maxh = 1001; int dp[2][maxn][maxh]; bool vis[maxn][maxh]; char opt[50][maxn][maxh]; Node pre[50][maxn][maxh]; int optp[50][maxn][maxh]; void print_ans(int i,Node rt) { printf("VICTORIOUS\n"); stack<Node> S; S.push(rt); while(i>0){ Node &u = S.top(); S.push(pre[i][u.first][u.second]); i--; } while(S.size()){ Node &u = S.top(); char ch = opt[i][u.fi][u.se]; putchar(ch); if(ch == 'T') printf(" %d",optp[i][u.first][u.second]); putchar('\n'); S.pop(); i++; } } void updata(int i,int nps,int Hm,char op,int opp,Node &u,int val,int (*nex)[maxh]) { opt[i][nps][Hm] = op;//操作指令 optp[i][nps][Hm] = opp;//传送相关 pre[i][nps][Hm] = u;//前驱状态 nex[nps][Hm] = val;//更新val值 } void solve() { vector<Node> V1,V2,*q1 = &V1,*q2 = &V2; q1->PB({N,Nm*HPm}); dp[0][N][Nm*HPm] = HPh; for(int i = 0; i < MPh; i++){ memset(vis,0,sizeof(vis)); int (*d1)[maxh] = dp[i&1], (*d2)[maxh] = dp[(i&1)^1];//当前状态,和上一层状态 for(int j = 0; j < (int)q1->size(); j++){ Node &u = q1->at(j); //雷击 int nps = max(u.fi-V,1);//怪兽移动后的位置 next_pos int tHp = max(u.se-L[u.fi],0);//攻击后怪兽的HP int val = d1[u.fi][u.se];//英雄血量 int damege = 0; if(nps == 1){ damege = (tHp+HPm-1)/HPm;}//怪兽移动后到达1号格子,对英雄攻击 if(val-damege>0 || tHp == 0){//英雄能承受攻击 或者 怪物已经死了 if(!vis[nps][tHp]){//状态判重 vis[nps][tHp] = true, q2->PB({nps,tHp}); updata(i,nps,tHp,'L',-1,u,val-damege,d2); if(tHp == 0) { print_ans(i,Node(nps,0)); return; } }else if(d2[nps][tHp] < val-damege){ updata(i,nps,tHp,'L',-1,u,val-damege,d2); if(tHp == 0) { print_ans(i,Node(nps,0)); return; } } } //传送 for(int tp = 2; tp <= N; tp++){//传送到tp位置 int tnps = max(tp-V,1);//怪物移动 int tval = d1[u.first][u.second]; if(tnps == 1){ tval -= (u.se+HPm-1)/HPm; } if(tval>0){ if(!vis[tnps][u.second]){ vis[tnps][u.se] = true,q2->PB({tnps,u.se}); updata(i,tnps,u.se,'T',tp,u,tval,d2); }else if(d2[tnps][u.second] < tval){ updata(i,tnps,u.se,'T',tp,u,tval,d2); } } } if(nps == 1){ damege = (u.se+HPm-1)/HPm;}//未使用雷击时怪兽造成的伤害 else damege = 0; val = min(d1[u.fi][u.se]+dH,HPh);//治疗 if(val>damege){ val -= damege; if(!vis[nps][u.se]){ vis[nps][u.se] = true; q2->PB({nps,u.se}); updata(i,nps,u.se,'H',-1,u,val,d2); }else if(d2[nps][u.se] < val){ updata(i,nps,u.se,'H',-1,u,val,d2); } } } q1->clear(); swap(q1,q2); } puts("DEFEATED"); } int main() { freopen("heroes.in","r",stdin); freopen("heroes.out","w",stdout); cin>>N>>HPh>>MPh>>HPm>>Nm>>V>>dH; for(int i = 1; i <= N; i++) scanf("%d",L+i); solve(); return 0; }