• [AHOI2014/JSOI2014]骑士游戏(SPFA认识)


    传送门

    好题!考察了对于SPFA的深刻理解。

    对于每个怪,我们要么花费魔攻代价,要么花费普攻代价+消灭其儿子的代价。

    看似像个一直递归下去的做法,仿佛可以用dp?

    但是图中可能会存在环,就会有后效性。

    假如我们用一个队列保存当前还需要消灭的怪。

    我们每次提出队首,要么魔攻,要么普攻然后又压入一堆新出现的怪。直到队为空。

    感觉这个过程有点像SPFA。

    SPFA是对于每次那些还可能松弛更新的边进行松弛,直到无法操作(队空)就保证了最短路。

    对于这道题,我们好像也可以每次看能不能再更新消灭怪的最小花费,直到不能再更新。

    即使有正环也不会影响最短路。

    如果连边,显然不是很好操作。不如考虑直接模拟SPFA的操作过程。

    dis[i]存的不是起点1到它的最短路,我们定义dis[i]为消灭i及其产物的最小花费

    我们考虑从某一个怪被魔攻(一定会有一些怪被魔攻,不然产生怪无止境),其对于父亲的dis会做出贡献。

    或者说一个怪不一定是魔攻,但消灭其及其产物的最小代价是dis[i],这个dis[i]会对父亲的dis做出贡献。

    把父亲的魔攻代价和普攻代价+消灭其儿子的代价相比进行更新操作。

    如果父亲被更新了,说明父亲的父亲也还有可能更新,于是把父亲的父亲再次入队。

    所以把dis[i]一开始就设为这个怪被魔攻的代价。

    我们保存一个点的儿子和它可以更新的父亲。

    跑一遍SPFA,最后输出dis[1]即可。

    #include<bits/stdc++.h>
    #define LL long long
    #define N 200003
    #define M 1000003
    using namespace std;
    LL read()
    {
        LL x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    struct EDEG{
        int nextt,to;
    }w[M];
    vector<int>fa[N];
    LL s[N],dis[N];
    int vis[N],head[N];
    int tot=0,n;
    void add(int a,int b)
    {
        tot++;
        w[tot].nextt=head[a];
        w[tot].to=b;
        head[a]=tot;
    }
    queue<int>q;
    void SPFA()
    {
        while(!q.empty())q.pop();
        for(int i=1;i<=n;++i)vis[i]=1,q.push(i);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            vis[x]=0;
            LL tmp=s[x];
            for(int i=head[x];i;i=w[i].nextt)
            {
                int v=w[i].to;
                tmp+=dis[v];
            }
            if(tmp>=dis[x])continue;
            dis[x]=tmp;
            for(int i=0;i<fa[x].size();++i)
            {
                int f=fa[x][i];
                if(vis[f])continue;
                vis[f]=1;q.push(f);
            }
        }
        printf("%lld
    ",dis[1]);
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;++i)
        {
            s[i]=read(),dis[i]=read();
            int num=read();
            while(num--)
            {
                int x=read();
                add(i,x);fa[x].push_back(i);
            }
        }
        SPFA();
    } 
    View Code
  • 相关阅读:
    鲁迅散文——随感录三十五
    跳一跳201803-1
    鲁迅散文——狗的驳诘
    鲁迅散文——立论
    小中大201903-1
    鲁迅散文——题辞
    小明上学201812-1
    买菜201809-2
    Linux常用命令-2
    LaTeX——基本介绍及字体设置
  • 原文地址:https://www.cnblogs.com/yyys-/p/11697472.html
Copyright © 2020-2023  润新知