题目链接:Monster Hunter
题意
小 Q Q 在一棵 个节点的树上,他最初的位置在节点 1 1 处,除了 以外,其他节点都有一个怪兽,小 Q Q 需要沿着树上路径行走,每到达一个节点 ,就要与这个节点上的怪兽进行战斗,战斗将会损失 ai a i 点 HP H P ,战斗过程中若 HP H P 值小于 0 0 则游戏结束,战斗结束后若小 的 HP H P 值不小于 0 0 ,则他的 值将会增加 bi b i ,每个节点上的怪兽被消灭后,再次走到这个节点时就不需要再进行战斗,问如果要消灭树上所有的怪兽,小 Q Q 最初至少需要有多少点 值。
输入
第一行为一个整数 T (1≤T≤2000) T ( 1 ≤ T ≤ 2000 ) ,接下去有 T T 组数据,每组数据第一行为一个整数 ,接下去 n−1 n − 1 行每行两个整数 ai,bi (0≤ai,bi≤109) a i , b i ( 0 ≤ a i , b i ≤ 10 9 ) ,第 i i 行整数表示节点 上的战斗 HP H P 增减值,接下去 n−1 n − 1 行每行两个整数 ui,vi (1≤ui,vi≤n) u i , v i ( 1 ≤ u i , v i ≤ n ) ,表示节点 ui u i 和 vi v i 之间有一条边,数据保证 ∑n≤106 ∑ 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 的怪兽必然要比 a≥b a ≥ b 的怪兽先进行攻击,这样才能获得最大的 HP H P ,在 a<b a < b 的所有怪兽中,必然要先攻击 a a 值小的,在 大的所有怪兽中,如果先攻击第 i i 只怪兽再攻击第 只怪兽,在攻击后一只怪兽的战斗过程中的 HP H P 值为 HP−ai+bi−aj H P − a i + b i − a j ,如果反过来先攻击第 j j 只怪兽,在后一只怪兽的战斗过程中的 值为 HP−aj+bj−ai H P − a j + b j − a i ,可以发现攻击的次序与 ai a i 和 aj a j 无关,而只与 bi b i 和 bj b j 有关,必然要先攻击 b b 值大的。
通过以上规则可以确定一个最优的攻击怪兽的次序 ,现在考虑树上路径的限制,攻击每只怪兽之前必须要先打败它所有的祖父节点,因此对于每个节点 pi p i ,判断该节点的父节点是否为 1 1 ,若为 ,则可以直接攻击,否则将 pi p i 的 ai,bi a i , b i 值合并到它的父节点的 af,bf a f , b f 上表示在攻击完它的父节点后必须立即攻击第 i i 个节点,父节点的 值应更新为
a′f=−min(−af,−af+bf−ai)b′f=−af+bf−ai+bi+a′f 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;
}
本文探讨了一个游戏问题,玩家需要在一个树状地图上战胜怪物。通过分析战斗收益和损失,提出了一种最优策略,并给出了解决方案的代码实现。
&spm=1001.2101.3001.5002&articleId=81583316&d=1&t=3&u=fac87d5e5b7f4ae8bec991c496b5c721)
1274

被折叠的 条评论
为什么被折叠?



