[AHOI2014/JSOI2014/一本通1722]骑士游戏 题解 (spfa做dp)

题目描述

在游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击。两种攻击方式都会消耗JYY一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。

游戏世界中一共有N种不同的怪兽,分别由1到N编号,现在1号怪兽入侵村庄了,JYY想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?

输入

第一行包含一个整数N。

接下来N行,每行描述一个怪兽的信息;

其中第i行包含若干个整数,前三个整数为Si,Ki和Ri,表示对于i号怪兽,普通攻击需要消耗Si的体力,法术攻击需要消耗Ki的体力。同时i号怪兽死亡后会产生Ri个新的怪兽。表示一个新出现的怪兽编号。同一编号的怪兽可以出现多个。

输出

输出一行一个整数,表示最少花费的体力值。

输入样例

4
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2

输出样例

26

提示

样例解释

首先用花费4点体力用普通攻击,然后出现的怪兽编号是2,2和3。再花费10点体力用法术攻击杀死两个编号为2的怪兽。剩下3号怪兽,花费1点体力用普通攻击。此时村庄里还有编号为2和4的怪兽。最后花费11点体力用法术攻击将这两只怪兽彻底杀死。一共花费的体力是\(4+5+5+1+5+6=26\)

数据规模

\(2≤N≤2×10^5 ,1≤R_i,ΣR_i≤10^6 ,1≤K_i,S_i≤5×10^{14}\)

主要思路

定义\(f_i\)表示完全清除第i个怪兽的体力。
不难得出 \(f_i=min(k_i,s_i+\underset{j表示i怪兽死亡后产生的新怪兽}{Σf_j})\)
而这个生成的怪兽链可能形成个环,所以我们不能用传统DP的方法做。而要使用spfa跑这个\(f_i\)
首先需要建一张图 \(G1\) ,\(i\)死后生成了\(j\)就可以连一条\(i->j\)的边
然后需要建一张反图,用来\(f_i\)更新时,让\(f_i\)的父节点入队更新。
更新条件是\(s_i+Σf_j<f_i\)
不难看出\(f_i\)的不可能大于\(k_i\),所以可以用\(k_i\)\(f_i\)初始化。

代码

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

typedef long long ll;

const int N = 2e5 + 128;
const int M = 1e6 + 128;

struct ios//快读
{
    inline char read()
    {
        static const int IN_LEN = 1 << 18 | 1;
        static char buf[IN_LEN], *s, *t;
        return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
    }

    template <typename _Tp>
    inline ios &operator>>(_Tp &x)
    {
        static char c11, boo;
        for (c11 = read(), boo = 0; !isdigit(c11); c11 = read())
        {
            if (c11 == -1)
                return *this;
            boo |= c11 == '-';
        }
        for (x = 0; isdigit(c11); c11 = read())
            x = x * 10 + (c11 ^ '0');
        boo && (x = -x);
        return *this;
    }
} io;

namespace G1
{
    int head[N], nxt[M], to[M];
    int p = 0;

    inline void add_edge(int f, int t)
    {
        to[++p] = t;
        nxt[p] = head[f];
        head[f] = p;
    }
}

namespace G2 //反图
{
    int head[N], nxt[M], to[M];
    int p = 0;

    inline void add_edge(int f, int t)
    {
        to[++p] = t;
        nxt[p] = head[f];
        head[f] = p;
    }
}

ll s[N], k[N];

ll f[N];

bool vis[N];

int n;

ll spfa()
{
    queue<int> Q;
    for (int i = 1; i <= n; i++)
        Q.push(i);
    while (!Q.empty())
    {
        int k = Q.front();
        Q.pop();
        vis[k] = false;

        ll sum = s[k];
        for (int i = G1::head[k]; i != 0; i = G1::nxt[i])
            sum += f[G1::to[i]];
        if (sum < f[k])
        {
            f[k] = sum;
            for (int i = G2::head[k]; i != 0; i = G2::nxt[i])//通知父节点
                if (!vis[G2::to[i]])
                    Q.push(G2::to[i]), vis[G2::to[i]] = true;
            ;
        }
    }
    return f[1];
}

int main()
{
    io >> n;
    for (int i = 1; i <= n; i++)
    {
        int r;
        io >> s[i] >> k[i] >> r;
        for (int j = 1; j <= r; j++)
        {
            int t;
            io >> t;
            G1::add_edge(i, t);
            G2::add_edge(t, i);
        }
        f[i] = k[i];
    }
    printf("%lld", spfa());
    return 0;
}
posted @ 2021-08-10 22:48  Icys  阅读(119)  评论(0编辑  收藏  举报