[AHOI2014/JSOI2014/一本通1722]騎士遊戲 題解 (spfa作dp)

題目描述

在遊戲中,JYY一共有兩種攻擊方式,一種是普通攻擊,一種是法術攻擊。兩種攻擊方式都會消耗JYY一些體力。採用普通攻擊進攻怪獸並不能把怪獸完全殺死,怪獸的屍體能夠變出其餘一些新的怪獸,注意一個怪獸可能通過若干次普通攻擊後變回一個或更多一樣的怪獸;而採用法術攻擊則能夠完全將一個怪獸殺死。ios

遊戲世界中一共有N種不一樣的怪獸,分別由1到N編號,如今1號怪獸入侵村莊了,JYY想知道,最少花費多少體力值才能將全部村莊中的怪獸所有殺死呢?git

輸入

第一行包含一個整數N。this

接下來N行,每行描述一個怪獸的信息;spa

其中第i行包含若干個整數,前三個整數爲Si,Ki和Ri,表示對於i號怪獸,普通攻擊須要消耗Si的體力,法術攻擊須要消耗Ki的體力。同時i號怪獸死亡後會產生Ri個新的怪獸。表示一個新出現的怪獸編號。同一編號的怪獸能夠出現多個。code

輸出

輸出一行一個整數,表示最少花費的體力值。遊戲

輸入樣例

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\)it

數據規模

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

主要思路

定義\(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\)初始化。class

代碼

#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;
}
相關文章
相關標籤/搜索