AcWing 4742. 电

某城市有 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≤109
1≤Xi,Yi≤N
一个测试点内最多 15 组数据满足 1≤N≤2×105,其余数据满足 1≤N≤103。

输入样例:
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条边,可知本题为树

目标函数:max{f[i]=1+f[1]+...+f[k]};

(f[i]代表i能到达的节点数,首先假定i能到达1-k个点)

所以本题只需要遍历所有节点,输出最大的f[i]值即可。

用到的算法:记忆化搜索DP

本题中题目中描述为树,所以无环,可以用DP搜索

若题中有环,则需要用图论的算法

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N=200010,M=2*N;

int h[N],e[M],ne[M],idx;
int w[N];
int f[N];
int 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[j]<w[u]) res+=dp(j);
    }
    f[u]=res;
    return res;
}

int main()
{
    int T;
    scanf("%d",&T);
    for(int k=1;k<=T;k++)
    {
        //int n;
        scanf("%d",&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",k,res);
    }
    return 0;
}
注意点

1.输入用scanf,scanf比cin所花费时间短

2.memset时可以不用把整个范围直接初始化,而是把用到的内容初始化

3.每次在计算时,都要把idx置为0

4.记忆化搜索固定代码

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[j]<w[u]) res+=dp(j);
    }
    f[u]=res;
    return res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值