树(网络,LA 3902)

可能是因为做了很多树形DP,而很少做其他题的缘故,我总希望通过一遍或几遍DFS来完成预处理以及计算答案。这样的思路是十分狭隘的。一开始想让树的重心或者中心来做根,然后dfs下去,找到一个最上面的点做服务器,使得下面所有叶子都能被照顾到。但是这样的话如何判断这个点以上的叶子是否被照顾到就十分难以解决。而且已有的那个服务器还会带来额外的麻烦。

自己对这种题目的应对实在是太死板了。
有一个值得学习的地方是部分dfs,就是选某个点,然后以他为根,对他进行限制深度的dfs,凡是访问过的地方就标记一个vis,这样就可以实现树的覆盖。每次选一个最深的待覆盖的点,然后在他的k级祖先上放服务器,就可以保证这个祖先的所有待覆盖儿子都能被覆盖到。然后对这个服务器跑一遍部分dfs,就能照顾到祖先以上的点。部分dfs时更新覆盖的点,以便下次再选一个没覆盖的最深的点。

判断某个点有没有被覆盖,只用一个vis数组。

选取最深的待覆盖点,可以用优先队列,也可以像大白书上那样push_back()到代表某个深度的vector里。

至于寻找待覆盖点,只用在一开始以s为根跑一遍dfs,dfs带上深度的参数,把所有深度大于k的叶保存起来就好了。


代码

#include<bits/stdc++.h>
#define maxn 1010
using namespace std;

int n,s,k;
vector<int>MAP[maxn];
vector<int>node[maxn];
int fa[maxn];
bool vis[maxn];

void dfs(int u,int f,int d)
{
    fa[u]=f;
    if(MAP[u].size()==1&&d>k)
        node[d].push_back(u);
    for(unsigned int i=0;i<MAP[u].size();i++)
    {
        int v=MAP[u][i];
        if(v==f) continue;
        dfs(v,u,d+1);
    }
}

void dfs2(int u,int f,int d)
{
    if(d>k) return;
    vis[u]=true;
    for(unsigned int i=0;i<MAP[u].size();i++)
    {
        int v=MAP[u][i];
        if(v==f) continue;
        dfs2(v,u,d+1);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        scanf("%d %d %d",&n,&s,&k);
        for(int i=0;i<=n;i++)
        {
            MAP[i].clear();
            node[i].clear();
        }
        int a,b;
        for(int i=1;i<n;i++)
        {
            scanf("%d %d",&a,&b);
            MAP[a].push_back(b);
            MAP[b].push_back(a);
        }
        dfs(s,-1,0);
        int ans=0;
        for(int i=n-1;i>k;i--)
            for(unsigned int j=0;j<node[i].size();j++)
            {
                int u=node[i][j];
                if(!vis[u])
                {
                    ans++;
                    for(int i=0;i<k;i++) u=fa[u];
                    dfs2(u,-1,0);
                }
            }
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值