[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;
}
原文地址:https://www.cnblogs.com/clockwhite/p/13451092.html