zoukankan      html  css  js  c++  java
  • 题解 P5022 【旅行】

    小 Y 是一个爱好旅行的 OIer。她来到 X 国,打算将各个城市都玩一遍。

    小Y了解到, X国的 (n) 个城市之间有 (m) 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些 道路从一个城市前往另一个城市。

    小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。

    为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 (n) 的序列。她希望这个序列的字典序 最小,你能帮帮她吗? 对于两个长度均为 (n) 的序列 (A)(B),当且仅当存在一个正整数 (x),满足以下条件时, 我们说序列 (A) 的字典序小于 (B)

    • 对于任意正整数 (1 ≤ i < x),序列 (A) 的第 (i) 个元素 (A_i) 和序列 (B) 的第 (i) 个元素 (B_i) 相同。

    • 序列 (A) 的第 (x) 个元素的值小于序列 (B) 的第 (x) 个元素的值。

    前置知识:

    这道题数据范围关键

    首先,对于一棵树,我们想求出字典序最优的方案肯定要每部都要尽量优。

    比如:

    这么一棵树,我们为了让字典序最优就得做些操作。

    变成这个样子:

    360截图18750814346171.png

    我们把它转换成代码:

    for(int i=n;i>=1;i--)sort(v[i].begin(),v[i].end());
    

    求出字典序最优的方案自然也是水到渠成了

    void dfs(int x,int fa,int y,int z){
    	s.push_back(x);
    	for(auto i:v[x])
    		if(i!=fa&&(i!=y||x!=z)&&(i!=z||x!=y))dfs(i,x,y,z);
    }
    

    那么对于基环树怎么办呢?

    比如这张图:

    360截图18750814346171.png

    怎么办呢?

    我们发现肯定有一条边是费的,再仔细想想,那条边一定在换上面。

    Y2lYpd.png

    蓝色线条上的就是环,我们怎么去找环呢,可以使用 ( exttt{tarjan}) 算法 去找环。

    找环的话先得建图

    图的话,我们可以进行一遍搜索

    void work(int x,int fa){
    	if(lk[x])return;
    	lk[x]=true;
    	for(auto i:v[x])
    		if(i!=fa){
    			E[x].push_back(i);
    			work(i,x);
    		}
    }
    

    这样图就建好了

    ( exttt{tarjan}) 的代码我也放一下

    void tarjan(int now){
    	dfn[now]=low[now]=++dfscnt;
    	sk[stop++]=now;
    	for (auto i:E[now]){
    		if(!dfn[i]){
    			tarjan(i);
    			low[now]=min(low[now],low[i]);
    		}else if(!sccnum[i]){
    			low[now]=min(low[now],dfn[i]);
    		}
    	}
    	if(dfn[now]==low[now]){
    		scccnt++;
    		do{
    			sccnum[sk[--stop]]=scccnt;
    		}while(sk[stop]!=now);
    	}
    }
    

    我们很容易地发现,环上的边的两个端点的 (sccnum) 是相同的。

    我们再去看看图

    Y21ZE8.png

    框出来的是 (7) 个强联通分量,我们发现除了部分的强联通分量大小超过 (1),其他强联通分量的大小都 (= 1)

    所以,一条边的 (2) 个端点的 (sccnum) 相同就表明这条边一定是在链上面

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    int n,m,x[5010],y[5010];
    vector<int>s,ans,v[5010],E[5010];
    void dfs(int x,int fa,int y,int z){
    	s.push_back(x);
    	for(auto i:v[x])
    		if(i!=fa&&(i!=y||x!=z)&&(i!=z||x!=y))dfs(i,x,y,z);
    }
    int sk[5010],stop,dfn[5010],low[5010],scccnt,sccnum[5010],dfscnt,lk[5010];
    void tarjan(int now){
    	dfn[now]=low[now]=++dfscnt;
    	sk[stop++]=now;
    	for (auto i:E[now]){
    		if(!dfn[i]){
    			tarjan(i);
    			low[now]=min(low[now],low[i]);
    		}else if(!sccnum[i]){
    			low[now]=min(low[now],dfn[i]);
    		}
    	}
    	if(dfn[now]==low[now]){
    		scccnt++;
    		do{
    			sccnum[sk[--stop]]=scccnt;
    		}while(sk[stop]!=now);
    	}
    }
    void work(int x,int fa){
    	if(lk[x])return;
    	lk[x]=true;
    	for(auto i:v[x])
    		if(i!=fa){
    			E[x].push_back(i);
    			work(i,x);
    		}
    }
    int main(){
    	read(n);read(m);
    	for(int i=1;i<=m;i++){
    		read(x[i]);read(y[i]);
    		v[x[i]].push_back(y[i]);
    		v[y[i]].push_back(x[i]);
    	}
    	for(int i=n;i>=1;i--)ans.push_back(i),sort(v[i].begin(),v[i].end());
    	work(1,0);
    	for(int i=1;i<=n;i++)
    		if(!dfn[i])tarjan(i);
    	if(m==n-1){
    		dfs(1,0,0,0);
    		ans=min(ans,s);
    	}else{
    		for(int i=1;i<=m;i++)	
    			if(sccnum[x[i]]==sccnum[y[i]]){
    				s.clear();
    				dfs(1,0,x[i],y[i]);
    				ans=min(ans,s);
    			}
    	}
    	for(auto i:ans)cout<<i<<" ";
    	return 0;
    }
    
  • 相关阅读:
    练习_Python3 爬取笔趣阁最新小说章节
    Python3 map()函数
    Java图片验证码生成
    神经网络
    leetcode
    hive开发规范
    北明数科 bug
    JAVA集合~
    令人头痛的JVM
    重定向和管道符
  • 原文地址:https://www.cnblogs.com/zhaohaikun/p/13830126.html
Copyright © 2011-2022 走看看