BZOJ3875--骑士游戏(SPFA处理带后效性的动态规划)
3875: [Ahoi2014]骑士游戏
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 181 Solved: 91
[Submit][Status][Discuss]
Description
【故事背景】
长期的宅男生活中,JYY又挖掘出了一款RPG游戏。在这个游戏中JYY会
扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽。
【问题描述】
在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻
击。两种攻击方式都会消耗JYY一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统bug,并不保证这一点)。
游戏世界中一共有N种不同的怪兽,分别由1到N编号,现在1号怪兽入
侵村庄了,JYY想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?
Input
第一行包含一个整数N。
接下来N行,每行描述一个怪兽的信息;
其中第i行包含若干个整数,前三个整数为Si,Ki和Ri,表示对于i号怪兽,
普通攻击需要消耗Si的体力,法术攻击需要消耗Ki的体力,同时i号怪兽死亡后会产生Ri个新的怪兽。表示一个新出现的怪兽编号。同一编号的怪兽可以出现多个。
Output
输出一行一个整数,表示最少需要的体力值。
Sample Input
4
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2
Sample Output
26
HINT
【样例说明】
首先用消耗4点体力用普通攻击,然后出现的怪兽编号是2,2和3。花费
10点体力用法术攻击杀死两个编号为2的怪兽。剩下3号怪兽花费1点体力进
行普通攻击。此时村庄里的怪兽编号是2和4。最后花费11点体力用法术攻击
将这两只怪兽彻底杀死。一共花费的体力是4+5+5+1+5+6=26。
【数据范围】
2<=N<=2*10^5,1<=Ri,Sigma(Ri)<=10^6,1<=Ki,Si<=5*10^14
分析:
首先要明白,SPFA用到了动态逼近(动态规划?)的思想,但它的动态是有后效性的,即一个点出队后,其最短路的值并未完全确定,可能后面还会对它进行松弛再次入队
这个题目其实是可以写出状态转移方程的:
令f[i]为杀死i号怪物的最小花销,则
f[i]=min(k[i],s[i]+Σf[j])
其中j为i用普通攻击后可以分裂为的怪物 ,k[i]为使用法术攻击,s[i]为使用普通攻击
要求杀死1号怪物的最小花费,最终结果即为f[1]
但是直接DP有后效性(状态之间相互依赖,并不是单向依赖),因此我们用SPFA来跑这个DP即可
初始时要把所有点入队,因为它们都可能被更新。
下面的代码中建立了两个邻接表,分别记录某个点的父亲与儿子(e[]与E[]),方便spfa
1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 #define N 201000 7 #define M 2010000 8 #define inf 0x3f3f3f3f 9 using namespace std; 10 struct KSD 11 { 12 int v,next; 13 }e[M],E[M]; 14 int head[N],HEAD[N],cnt; 15 inline void add(int u,int v) 16 { 17 e[++cnt].v=v; 18 E[cnt].v=u; 19 e[cnt].next=head[u]; 20 E[cnt].next=HEAD[v]; 21 HEAD[v]=head[u]=cnt; 22 23 } 24 long long A[N],dist[N]; 25 bool in[N]; 26 int n; 27 queue<int>q; 28 void spfa() 29 { 30 while(!q.empty())q.pop(); 31 32 int i,u,v; 33 for(i=1;i<=n;i++)q.push(i),in[i]=1; 34 while(!q.empty()) 35 { 36 u=q.front(),q.pop(),in[u]=0; 37 long long temp=A[u]; 38 for(i=head[u];i;i=e[i].next) 39 temp+=dist[e[i].v]; 40 if(temp>=dist[u])continue; 41 dist[u]=temp; 42 for(i=HEAD[u];i;i=E[i].next) 43 if(!in[v=E[i].v])q.push(v),in[v]=1; 44 } 45 } 46 int main() 47 { 48 int i,j,k; 49 int a,b,c; 50 scanf("%d",&n); 51 for(i=1;i<=n;i++) 52 { 53 cin>>A[i]>>dist[i]>>c; 54 while(c--) 55 { 56 scanf("%d",&a); 57 add(i,a); 58 } 59 } 60 spfa(); 61 cout<<dist[1]; 62 return 0; 63 }