题意:
分析:看到这道题目,我们直观的感受(贪心思考),对于一个节点i,如果i的祖先节点的花费比它小,显然可以让i作为这个祖先节点的子树中的某个节点,因为我们有一个高度更高并且花费更小的节点在它之上,并且管辖它。然后,我们还要考虑不行的局面,对于0-0、1-1的类型,我们没有必要去改动它,我们只需要改动0-1、1-0这种类型的节点,如果总的0-1、1-0不配对(奇偶性不一样),那么就是不可行的局面。我们自底向上的处理每个子树中0-1,1-0的对,看是否能配对,否则作为返回值返回给上面处理。
这道题的想法思路不难,但是对设计搜索的能力要求非常高,等会我会继续编辑讲解怎么设计dfs。
根据我的经验,首先设计dfs的时候,要把循环分支的子问题当作已经处理好的,然后返回值是处理好多余的配对,如果上面存在一个花费更小,更高的节点,我们这个子树中的配对数量就不要配对了,往上传递再配对,还有就是本层节点的配对数也要加上,这样,基本上都能处理好dfs。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200005;
typedef pair<int, int> PII;
int cost[N], b[N], c[N];
int h[N], e[2 * N], ne[2 * N], idx;
long long res;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
PII dfs(int u, int parent, int mn)
{
//本层的遗留配对数,作为返回
PII a = { 0, 0 };
//初值和终值不一样
if (b[u] != c[u])
{
if (b[u])
++a.first;
else
++a.second;
}
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (j == parent) continue;
PII p = dfs(j, u, min(mn, cost[u]));
//加上从下面返回过来的遗留配对数
a.first += p.first;
a.second += p.second;
}
//计算,返回遗留配对数,并且判断是否能被u管辖
if (cost[u] < mn)
{
int take = min(a.first, a.second);
res += 2LL * take * cost[u];
a.first -= take;
a.second -= take;
}
return a;
}
int main()
{
//n个节点
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d%d%d", &cost[i], &b[i], &c[i]);
}
memset(h, -1, sizeof h);
int u, v;
for (int i = 1; i < n; ++i)
{
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
pair<int, int> ans = dfs(1, 0, 2e9);
if (ans.first > 0 || ans.second > 0)
{
puts("-1");
}
else
{
printf("%lld\n", res);
}
return 0;
}