zoukankan      html  css  js  c++  java
  • 洛谷 题解 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;
    }
    
  • 相关阅读:
    hive查询语句合并问题
    hive isnull或ifnull的替代方法if()方法
    hive科学计数法引发的问题
    科学计数法转字符串
    shell命令执行结果$?
    shell脚本中变量接受hive语句的返回值问题
    shell简单命令
    js切换图片
    js点击图片切换
    操作节点
  • 原文地址:https://www.cnblogs.com/hulean/p/10799265.html
Copyright © 2011-2022 走看看