luogu P4042 [AHOI2014/JSOI2014]骑士游戏
这道题首先想到Dp。。
不妨设 f[i] 表示杀死怪兽 i 需要的最少体力, 那么有 f[i] = min( k[i] , s[i] + ∑ f [ri] ) , 这还是很好想的。。
但是没有办法转移啊,因为可能有环,高斯消元??,不存在的。。
然后就开始搞最短路了,但怎样建图呢,一般的跑最短路都是有起点,有终点,有边权,但这道题除了知道起点是 1 外,其他啥也没有。。
于是我们就需要深入理解SPFA的本质,所以说这是一道很好的思维题。。。
SPFA是去不断的找最优解(松弛)。。。。。
那么我们就可以通过不断比较 f[i] 与 s[i] + Σf[ri] 来进行松弛,若后者更小,则令 f[i] = s[i] + Σf[ri] ,并用 f[i] 去更新其他的 f ,
即将能够产生它的怪物入队,当然若已经在队列里就不用再进队啦,和SPFA是一样的。
至于 f[i] 的初值,赋成 k[i] 即可。
所以我们要建两个图,一个是它能够产生的怪物,用来更新自己,另一个是能够产生它的怪物,用来更新其他怪物。。
最后 f[1] 就是答案啦。。
Code
#include<iostream> #include<cstdio> #include<queue> #define N 200010 #define M 1000010 #define ll long long using namespace std; queue<int> q; int n,r[N],Head1[M],ver1[M*2],nex1[M*2],tot1,Head2[M],ver2[M],nex2[M],tot2; ll k[N],s[N],f[N]; bool v[N]; inline int read(){ char c=getchar();int x=0,flag=1; while(c<'0' || c>'9'){if(c=='-') flag=-1;c=getchar();} while(c>='0' && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar(); return x*flag; } void add1(int x,int y){ver1[++tot1]=y;nex1[tot1]=Head1[x];Head1[x]=tot1;} void add2(int x,int y){ver2[++tot2]=y;nex2[tot2]=Head2[x];Head2[x]=tot2;} void SPFA(){ while(q.size()){ int x=q.front();q.pop();v[x]=0; ll tmp=s[x]; for(int i=Head1[x];i;i=nex1[i]) tmp+=f[ver1[i]]; if(tmp<f[x]){ f[x]=tmp; for(int i=Head2[x];i;i=nex2[i]){ if(!v[ver2[i]]) q.push(ver2[i]),v[ver2[i]]=1; } } } } int main(){ n=read(); for(int i=1;i<=n;i++){ scanf("%lld%lld%d",&s[i],&k[i],&r[i]);f[i]=k[i];q.push(i);v[i]=1; for(int j=1;j<=r[i];j++){int x=read();add1(i,x);add2(x,i);} } SPFA(); printf("%lld\n",f[1]); }