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]);
}

 

posted @ 2019-10-16 08:54  dzzx_Syh  阅读(115)  评论(0编辑  收藏  举报