HDU 6326 Monster Hunter(贪心)

本文探讨了一个游戏问题,玩家需要在一个树状地图上战胜怪物。通过分析战斗收益和损失,提出了一种最优策略,并给出了解决方案的代码实现。

题目链接:Monster Hunter

题意

Q Q 在一棵 n 个节点的树上,他最初的位置在节点 1 1 处,除了 1 以外,其他节点都有一个怪兽,小 Q Q 需要沿着树上路径行走,每到达一个节点 i,就要与这个节点上的怪兽进行战斗,战斗将会损失 ai a i HP H P ,战斗过程中若 HP H P 值小于 0 0 则游戏结束,战斗结束后若小 Q HP H P 值不小于 0 0 ,则他的 HP 值将会增加 bi b i ,每个节点上的怪兽被消灭后,再次走到这个节点时就不需要再进行战斗,问如果要消灭树上所有的怪兽,小 Q Q 最初至少需要有多少点 HP 值。

输入

第一行为一个整数 T (1T2000) T   ( 1 ≤ T ≤ 2000 ) ,接下去有 T T 组数据,每组数据第一行为一个整数 n (2n105),接下去 n1 n − 1 行每行两个整数 ai,bi (0ai,bi109) a i , b i   ( 0 ≤ a i , b i ≤ 10 9 ) ,第 i i 行整数表示节点 i+1 上的战斗 HP H P 增减值,接下去 n1 n − 1 行每行两个整数 ui,vi (1ui,vin) u i , v i   ( 1 ≤ u i , v i ≤ n ) ,表示节点 ui u i vi v i 之间有一条边,数据保证 n106 ∑ n ≤ 10 6

输出

输出通关所需要的最小的初始 HP H P 值。

样例

输入
1
4
2 6
5 4
6 2
1 2
2 3
3 4
输出
3
题解

首先不考虑树上路径的限制,如果每个怪兽可以以任意次序进行战斗, a<b a < b 的怪兽必然要比 ab a ≥ b 的怪兽先进行攻击,这样才能获得最大的 HP H P ,在 a<b a < b 的所有怪兽中,必然要先攻击 a a 值小的,在 ab 大的所有怪兽中,如果先攻击第 i i 只怪兽再攻击第 j 只怪兽,在攻击后一只怪兽的战斗过程中的 HP H P 值为 HPai+biaj H P − a i + b i − a j ,如果反过来先攻击第 j j 只怪兽,在后一只怪兽的战斗过程中的 HP 值为 HPaj+bjai H P − a j + b j − a i ,可以发现攻击的次序与 ai a i aj a j 无关,而只与 bi b i bj b j 有关,必然要先攻击 b b 值大的。
通过以上规则可以确定一个最优的攻击怪兽的次序 p1,p2,,pn1,现在考虑树上路径的限制,攻击每只怪兽之前必须要先打败它所有的祖父节点,因此对于每个节点 pi p i ,判断该节点的父节点是否为 1 1 ,若为 1,则可以直接攻击,否则将 pi p i ai,bi a i , b i 值合并到它的父节点的 af,bf a f , b f 上表示在攻击完它的父节点后必须立即攻击第 i i 个节点,父节点的 a,b 值应更新为

af=min(af,af+bfai)bf=af+bfai+bi+af a f ′ = − min ( − a f , − a f + b f − a i ) b f ′ = − a f + b f − a i + b i + a f ′
然后将所有 ai a i 的子节点的父节点改为 f f ,更新父节点并快速排序的过程可以用堆来维护,更改父节点的操作可以用并查集来维护。

过题代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <functional>
#include <iomanip>
using namespace std;

#define LL long long
const int maxn = 100000 + 100;
struct Node {
    LL a, b;
    int fa, Index;
};

bool operator<(const Node &a, const Node &b) {
    bool flag_a = a.a < a.b;
    bool flag_b = b.a < b.b;
    if(flag_a != flag_b) {
        return flag_a < flag_b;
    }
    if(flag_a) {
        return a.a > b.a;
    }
    return a.b < b.b;
}

bool operator!=(const Node &a, const Node &b) {
    return a.a != b.a || a.b != b.b || a.fa != b.fa || a.Index != b.Index;
}

int T, n, u, v;
int fa[maxn];
Node node[maxn];
vector<int> G[maxn];
priority_queue<Node> que;

void Init() {
    for(int i = 1; i <= n; ++i) {
        fa[i] = i;
        G[i].clear();
    }
}

int Find(int x) {
    return x == fa[x]? x: fa[x] = Find(fa[x]);
}

void unit(int x, int y) {
    int xx = Find(x);
    int yy = Find(y);
    fa[xx] = yy;
}

void dfs(int f, int x) {
    node[x].fa = f;
    int len = G[x].size();
    for(int i = 0; i < len; ++i) {
        int pos = G[x][i];
        if(pos != f) {
            dfs(x, pos);
        }
    }
}

int main() {
    #ifdef LOCAL
        freopen("test.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    #endif // LOCAL
    ios::sync_with_stdio(false);

    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        Init();
        node[1].a = node[1].b = 0;
        node[1].Index = 1;
        for(int i = 2; i <= n; ++i) {
            scanf("%I64d%I64d", &node[i].a, &node[i].b);
            node[i].Index = i;
        }
        for(int i = 1; i < n; ++i) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1, 1);
        for(int i = 2; i <= n; ++i) {
            que.push(node[i]);
        }
        while(!que.empty()) {
            Node tmp = que.top();
            que.pop();
            if(node[tmp.Index] != tmp || tmp.Index == 1) {
                continue;
            }            int f = Find(tmp.fa);
            unit(tmp.Index, f);
            LL a = -min(-node[f].a, -node[f].a + node[f].b - tmp.a);
            LL b = -node[f].a + node[f].b - tmp.a + tmp.b + a;
            node[f].a = a;
            node[f].b = b;
            que.push(node[f]);
        }
        printf("%I64d\n", node[1].a);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值