• [Noip2018]旅行(数据加强版)


    [Noip2018]旅行(数据加强版)

    一.前言

    ​ 之前有写过原版题目((O(n^2)))的题解,挂一个链接,然后这次肝了一下加强版,加强版题目链接。没有判是不是fa调了一下午qwq……

    二.思路

    ​ 我是标准的部分分写法,只是一个树的话很好做,直接每次贪最小就好,这里不加赘述。

    vector<int> q[MAXN];
    void dfs60(int x,int fa){
    	printf("%d ",x);
    	sort(q[x].begin(),q[x].end());
    	for(int i=0;i<q[x].size();++i){
    		if(q[x][i]==fa)continue;
    		dfs60(q[x][i],x);
    	}
    }
    

    然后是 (m=n) 的基环树。基环树,顾名思义,是基于一个环的树,简单来讲就是将树上两个点连起来,会形成一个环,断掉这个环上任意一边还是树……

    ​ 定义都不太重要,从中也许能得到n方做法,但是是过不了加强版的(n方移步原版),在加强版中,我选择直接贪心过。在本做法中,关于基环树,只需要知道一个点:我可以反悔一次。具体来讲,就是不走接下来的在环上的另一个点 v,而这个反悔仅限于环上的点 u。

    ​ 进一步解释,之前是树的时候,我没得选,

    必须乖乖的把自己的子树走完。而现在有了环,我可以有一次选择不继续走下去,而是往回走,从环的另一个方向走到本该走的点(下面用图画举例)

    ​ 也就是从红点到蓝点我有两条路可以走(看箭头),如果我此时反悔不走红线,我还有走蓝色这条线的机会。

    ​ 明确了这仅有一次的操作后,再来看另一个。
    咱现在在红点做出了不走蓝点反悔走父亲灰点的决策,那么在这个决策中,父亲灰点有已经走过的黑点儿子和没有走过的黄点儿子,红点也有没有走过的黄点儿子。

    ​ 那么在反悔回溯的过程中,我们必须由下至上将所有的黄儿子都走了才行,不然完不成走完所有的要求。

    ​ 有了上面的铺垫,这道题就可以爆切了。只需要维护一个反悔后第一个要走的黄儿子就行。判断它和接下来要走的蓝点哪个大,如果蓝点更大就反手一个回溯解决问题。关于黄儿子的维护:如果当前点(红点)按照从小到大依次遍历儿子,到了下一个点是在环上的蓝点,且还有儿子没有走(黄点),那么更新维护的黄儿子。特别的,如果没有儿子可以走了,从父亲哪里继承。(代码会清晰一点)

    ​ 最后涉及到一个找环的问题(可以乱写),我采用深搜找环,压栈的方式,这里看每个人有不同。

    三.CODE

    int stack[MAXN],top;
    bool vis[MAXN],ish[MAXN],fl;
    void findh(int x,int fa){
    	if(fl)return ;
    	vis[x]=1;stack[++top]=x;//压栈
    	for(int i=0;i<q[x].size();++i){
    		if(q[x][i]==fa)continue;
    		if(vis[q[x][i]]){//有已经遍历到的了,出现环
    			ish[q[x][i]]=1;
    			while(top&&stack[top]!=q[x][i]){
    				ish[stack[top--]]=1;//记录环
    			}
    			fl=1;
    			return ;
    		}
    		findh(q[x][i],x);
    		if(fl)return ;
    	}
    	top--;//弹栈
    }
    int mx=MAXN,alf;//mx是维护的黄儿子
    void dfs100(int x,int fa){
    	vis[x]=1;
    	printf("%d ",x);
    	sort(q[x].begin(),q[x].end());//排序以便从小到大
    	for(int i=0;i<q[x].size();++i){
    		if(q[x][i]==fa||vis[q[x][i]])continue;//父亲或者一次性不反悔将环走完了
    		if(!ish[q[x][i]])dfs100(q[x][i],x);//下一个点不在环上维护就是了
    		else {
    			if(!alf){//有没有反悔过的标记
    				int t=((i==q[x].size()-1)?mx:q[x][i+1]);
                    //取没有遍历过的下一个,没有就继承父亲
    				if(t==fa)mx=((i==q[x].size()-2)?mx:q[x][i+2]);
                    //这里特别注意(我调了很久才找到),判断是否是父亲
    				else mx=t;
    			}
    			if(mx<q[x][i]&&!alf){
    				alf=1;continue;//反悔
    			}
    			else dfs100(q[x][i],x);//走就完了
    		}
    	}
    }
    int main(){
    	n=read();m=read();
    	for(int i=1,x,y;i<=m;++i){
    		x=read();y=read();
    		q[y].push_back(x);
    		q[x].push_back(y);
    	}
    	if(m==n-1)dfs60(1,0);
    	else{
    		findh(1,0);//找环
    		memset(vis,0,sizeof(vis));
    		dfs100(1,0);//开始旅行
    	}
    	return 0;
    }
    
  • 相关阅读:
    redis集群规范
    mongodb的基本使用
    redis进阶
    redis基本使用
    selenium的基本使用
    C++入门
    C语言入门
    MATLAB中矩阵reshape的顺序规律
    Tensorflow:ImportError: DLL load failed: 找不到指定的模块 Failed to load the native TensorFlow runtime
    差分定位和精密定位
  • 原文地址:https://www.cnblogs.com/clockwhite/p/13451092.html
Copyright © 2020-2023  润新知