[BZOJ3875][Ahoi2014&Jsoi2014]骑士游戏(SPFA)

博客围绕一个问题展开,给出题目地址https://www.lydsy.com/JudgeOnline/problem.php?id=3875 ,提出设f[i]表示彻底杀死第i个怪兽所需的最小体力值,并指出f之间存在一定关系,涉及SPFA相关解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=3875

Solution

很容易想到设 f[i] f [ i ] 表示彻底杀死第 i i 个怪兽所需的最小体力值。
可以得出 f 之间满足:

f[i]=min(Ki,Si+ijf[j]) f [ i ] = min ( K i , S i + ∑ 怪 兽 i 死 亡 后 会 产 生 怪 兽 j f [ j ] )

考虑如果怪兽 i i 死亡后会产生怪兽 j ,那么就连边 <i,j> < i , j > <script type="math/tex" id="MathJax-Element-632"> </script> 。
这个图可能有环,故直接转移行不通。
我们考虑用 SPFA 来实现转移。
首先, f[i] f [ i ] 一定不超过 Ki K i ,故我们先把所有的 f[i] f [ i ] 设为 Ki K i
然后把所有的 i i 加入队列。
每一次,取出队头元素 i
进行检查,如果 Si+f[j] S i + ∑ f [ j ] 能够更新 f[i] f [ i ] ,那么就把 f[i] f [ i ] 设成 Si+Σf[i] S i + Σ f [ i ]
注意到 f[i] f [ i ] 被更新之后,如果存在边 <j,i> < j , i > <script type="math/tex" id="MathJax-Element-644"> </script> ,那么 f[j] f [ j ] 的值也会被更新。
故我们还要把 j j 加入队尾(如果已经在队中就不要加),使得 f[j] 能够在后面被更新。
最后答案 f[1] f [ 1 ]

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define Egde(u) for (int e = adj2[u], v = go2[e]; e; e = nxt2[e], v = go2[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
inline ll readll() {
    ll res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, M = 1e6 + 5, L = 15e6 + 5;
int n, R[N], ecnt, nxt[M], adj[N], go[M], ecnt2, nxt2[M],
adj2[N], go2[M], que[L];
ll f[N], S[N], K[N];
bool vis[N]; 
void add_edge(int u, int v) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
void add_edge2(int u, int v) {
    nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v;
}
ll SPFA() {
    int i, H = 0, T = n;
    For (i, 1, n) f[que[i] = i] = K[i], vis[i] = 1;
    while (H < T) {
        int u = que[++H]; vis[u] = 0;
        ll sum = S[u];
        Edge(u) sum += f[v];
        if (sum >= f[u]) continue;
        f[u] = sum;
        Egde(u) if (!vis[v]) vis[que[++T] = v] = 1;
    }
    return f[1];
}
int main() {
    int i, j, x;
    n = read();
    For (i, 1, n) {
        S[i] = readll(); K[i] = readll(); R[i] = read();
        For (j, 1, R[i]) x = read(), add_edge(i, x), add_edge2(x, i);
    }
    cout << SPFA() << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值