白魔法师

题目连接:链接:https://ac.nowcoder.com/acm/problem/205460
来源:牛客网
描述

你是一个白魔法师。
现在你拿到了一棵树,树上有 个点,每个点被染成了黑色或白色。
你可以释放一次魔法,将某个点染成白色。(该点不一定是黑色点,也可以是白色点)
现在释放魔法后要保证最大的白色点连通块尽可能大。请求出最大白色连通块的大小。
注:所谓白色连通块,指这颗树的某个连通子图,上面的点全部是白色。

输入

第一行输入一个正整数 ,代表树的顶点数量。
第二行输入一个长度为 的、仅由’W’和’B’组成的字符串,第 个点为’W’代表该点为白色,'B’代表该点为黑色。
接下来的 行,每行输入两个正整数 和 ,代表 点和 点有一条边连接。

输出

一个正整数,代表施放魔法后,最大的白色连通块的大小。

题意:
给定一颗树,树上每个点都有黑或白一个颜色,问把一个点变成白色(原本白色也可以)一个白色联通块最多有多少结点;

思路:
一开始在做题目时想多了,把问题的解决复杂化了——通过两遍dfs来求与某个结点相连的白色点和解决最大值。第一个bfs运用子节点向下更新,求出每个结点子节点白色联通块内结点数量;第二个bfs通过父节点的信息向下跟新子节点“上面”有多少白色联通的结点。每个结点(选择将其变白)联通白色结点数量为 up[u]+down[u]+1(结点上面的+结点下面的+本身),根据此值可求得最值;
赛后看完题解发现,可直接用并查集求出每个白色连通块的数量。然后枚举黑色的点,则把这个点变白后新连通块内结点数量为所有与其直接相连结点所在连通块的节点数的和+1,最后考虑没有黑色,则答案为节点数n

树形dp做法?:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Ms=1e5+5;
vector<int>G[Ms];
int up[Ms],down[Ms];
int son[Ms];
int mx=0;
char s[Ms];
int dfs1(int u,int fa)
{
    int sz=G[u].size();
    int v;
    son[u]=sz-1;
    for(int i=0;i<sz;i++)
    {
        v=G[u][i];
        if(v!=fa)down[u]+=dfs1(v,u);
    }
    if(s[u]=='W')return down[u]+1;
    else return 0;
}
void dfs2(int u,int fa)
{
    int v;
    if(up[u]+down[u]+1>mx)mx=up[u]+down[u]+1;
    for(int i=0;i<=son[u];i++)
    {
        v=G[u][i];
        if(v!=fa)
        {
            if(s[u]=='W')
            {
                up[v]=up[u]+down[u]+1;
                if(s[v]=='W')up[v]-=down[v]+1;
            }
            dfs2(v,u);
        }
    }
}
int main()
{
    int n,u,v;
    cin>>n;
    scanf("%s",s+1);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,-1);
    dfs2(1,-1);
    printf("%d\n",mx);
    return 0;
}

并查集法:

#include<bits/stdc++.h>
using namespace std;
const int Ms=1e5+5;
int st[Ms],cnt[Ms];
vector<int>G[Ms];
int find_top(int x)
{
    int top=x,t,j=x;
    while(top!=st[top])top=st[top];
    while(top!=j)
    {
        t=st[j];
        st[j]=top;
        j=t;
    }
    return top;
}
char s[Ms];
int main()
{
    int n,u,v;
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)st[i]=i,cnt[i]=s[i]=='W'?1:0;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
        if(s[u]==s[v]&&s[v]=='W')
        {
            u=find_top(u);
            v=find_top(v);
            if(v!=u)
            {
                st[v]=u;
                cnt[u]+=cnt[v];
            }
        }
    }
    int mx=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='B')
        {
            int tmp=1,sz=G[i].size();
            for(int j=0;j<sz;j++)
            {
                v=G[i][j];
                tmp+=cnt[find_top(v)];
            }
            mx=max(mx,tmp);
        }
    }
    if(mx==0)mx=n;
    printf("%d\n",mx);
    return 0;
}

若有什么错误,欢迎指正^ _ ^ 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值