来源:Google Kickstart2022 Round H Problem C
题目描述
某城市有 N个电力节点,编号 1∼N。
这些电力节点形成的电力网络,可以看作一个 N 个节点 N−1 条边的连通图。
每个电力节点都有一个固定的电容,其中第 i 个节点的电容为 Ai。
现在,可以选择其中一个节点进行供电,其它节点也可以根据实际连接以及具体电容情况接收电力。
具体来说,如果第 i 个节点通电,那么它也可以将电力传输给其它所有与它直接连接且电容严格小于 Ai 的节点。
我们希望通过合理选择初始供电节点,从而使得尽可能多的节点能够通电。
请你计算并输出可以通电的最大节点数量。
输入格式
第一行包含整数 T,表示共有 T组测试数据。
每组数据第一行包含整数 N。
第二行包含 N 个整数 A1,A2,…,AN。
接下来 N−1行,每行包含两个整数 Xi,Yi表示节点 Xi 和 Yi 之间存在直接连接。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y
,其中 x为组别编号(从 1 开始),y 为可以通电的最大节点数量。
数据范围
1≤T≤100,
1≤Ai≤10^9,
1≤Xi,Yi≤N,
一个测试点内最多 15 组数据满足 1≤N≤2×10^5,其余数据满足 1≤N≤10^3。
输入样例:
2
5
1 2 3 4 3
1 3
2 3
4 3
4 5
6
1 2 3 3 1 4
3 1
3 2
3 4
4 5
1 6
输出样例:
Case #1: 5
Case #2: 3
样例解释
在 Case 1 中,最佳方案是给第 4 个节点供电,这样可以将电力传输到所有节点。
注意,如果给第 3 个节点供电,则电力只会传输至第 1,2 个节点,而无法传输至第 4 个节点,这样只有三个节点可以通电。
在 Case 2 中,最佳方案是给第 3 个节点供电,这样可以将电力传输至第 1,2个节点,但是无法传输至第 4 个节点,因为 A4 并不严格小于 A3。
注意,如果给第6 个节点供电,则电力只会传输至第 1 个节点,如果给第 4 个节点供电,则电力只会传输至第 5 个节点。
解题思路
n个点,n-1条边,树形结构。
边为有向边,根据节点的电容大小决定方向。
求通电的最大节点数,即某个点满足:以该点为起点,联通点的数量最多。例如案例1,节点4为最优解,通电的最大节点数为w[4] = 1+ w[3] + w[5]。明显是一道记忆化搜索问题。
整体思路:存节点权值,存节点之间的边,从节点1开始到节点n,用记忆化搜索求出每个节点的联通点数。
完整代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5+10, M = N*2;
int n, T;
int h[N], e[M], ne[M], idx;
int w[N];
int f[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int dp(int u){
if(f[u] != -1) return f[u];
int res = 1;
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if(w[u] > w[j]) res += dp(j);
}
f[u] = res;
return res;
}
int main()
{
cin>>T;
for (int cases = 1; cases <= T; cases ++ ){
cin>>n;
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
memset(h, -1, (n + 1) * 4);
idx = 0;
for(int i=0;i<n-1;i++){
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b,a);
}
memset(f, -1, (n + 1) * 4);
int res = 0;
for(int i = 1; i <= n; i++){
res = max(res, dp(i));
}
printf("Case #%d: %d\n", cases, res);
}
return 0;
}