题解 P3942 【将军令】

本题算法:贪心+排序+搜索+并查集+图论

输入中的t可以不用管,毕竟这只是特殊情况的标志

题目中虽然没有很明确地说明这是一棵树,但是题目中说有n个点,但是只有n-1条边,想用这n-1条边把整个图连通起来,那么只有可能是棵树。(不信可以自己画画看)

竟然是一棵树了,那么就可以用找到每个节点唯一的父亲、爷爷、曾祖父、曾曾祖父、曾曾曾祖父......(此时可以开个数组记录一下每个节点的父亲)。

接下来,运用到了贪心的思想,因为一个小队能控制的最远距离为k,那么找当前最深的没有被访问过的节点的第k位祖宗所能覆盖的节点数才会更多。

找到了当前节点的第k位祖宗,就以这位祖宗为起点,用BFS向四周扩散k个单位,标记被扩散到的节点为已访问。

如果直接这样写,会出现一个问题

第19个点MLE---95分

究竟是哪出问题了呢?问题出在BFS身上,此时需要一个小小的剪枝技巧:

如果当前节点最大扩散出去的单位比现要求扩散的单位还要大的话,此节点就没必要进行扩散了。

具体细节详见代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+1;
int fa[MAXN];//记录父亲节点
int n,k,meiyouyong;
vector<int>ver[MAXN];//存图
struct Node
{
    int id;//记录当前节点编号,防止sort时打乱
    int deep;//当前节点的深度
}node[MAXN];
int bin[MAXN];//记录每个节点扩散的最大单位,用于剪枝
struct BFS
{
    int t;//当前遍历到的节点
    int cnt;//扩散cnt个单位
};
bool vis[MAXN];//是否遍历到过
inline int read()
{
    int tot=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        tot=tot*10+c-'0';
        c=getchar();
    }
    return tot;
}
inline void init(int now)//初始化
{
    for(int i=0;i<ver[now].size();i++)
    {
        int x=ver[now][i];
        if(node[x].deep)continue;
        node[x].deep=node[now].deep+1;//深度比父亲深1个单位
        node[x].id=x;
        fa[x]=now;
        init(x);
    }
}
inline int find(int now)//找到now节点的第k位祖宗
{
    int tot=now;
    for(int i=1;i<=k;i++)
    {
        tot=fa[tot];
    }
    return tot;
}
inline bool cmp(Node u,Node v)
{
    return u.deep>v.deep;
}
inline void bfs(int x)
{
    queue<BFS>q;
    q.push((BFS){x,k});
    vis[x]=1;
    while(q.size())
    {
        BFS now=q.front();
        if(now.cnt==0)break;//扩散结束
        q.pop();
        for(int i=0;i<ver[now.t].size();i++)
        {
            int x=ver[now.t][i];
            if(now.cnt<=bin[x])continue;//剪枝技巧
            bin[x]=now.cnt;//更新最大值
            vis[x]=1;//标记
            q.push((BFS){x,now.cnt-1});
        }
    }
}
int main()
{
    int x,y;
    n=read();k=read();meiyouyong=read();
    node[1].deep=1;
    fa[1]=1;
    node[1].id=1;
    for(int i=2;i<=n;i++)
    {
        x=read();y=read();
        ver[x].push_back(y);
        ver[y].push_back(x);
    }
    init(1);
    sort(node+1,node+1+n,cmp);
    /*for(int i=1;i<=n;i++)cout<<fa[i]<<" ";
    cout<<endl;*/
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(vis[node[i].id])continue;
        //若此节点已被遍历过,那么就没必要在去找一遍了
        int grand=find(node[i].id);
        //cout<<grand<<endl;
        bfs(grand);
        ans++;
        //cout<<i<<endl;
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hulean/p/10799265.html