NOIP2018 旅行

传送门

题意:60pts:给定一棵树,从某一个点出发,允许沿着来的路径返回。每次访问新城市,将标号加入到序列中,求字典序最小的序列。
另40pts:给定一棵基环树,从某一个点出发,允许沿着来的路径返回。每次访问新城市,将标号加入到序列中,求字典序最小的序列。

首先任何情况下都要先从1出发。
树的部分很好解决,每次从所在节点的子节点中选出最小的,并遍历其子树。
基环树的部分,可以发现这个环一定不会每条边都走,因此暴力枚举哪条边不走即可。注意判断删边之后的连通性
n=5000可过

using namespace std;
const int MAXN = 5005;
const int MAXM = 5005;

int n, m, num;
int ban;
int fir[MAXN], nxt[MAXM << 1], to[MAXM << 1], cnt;
int vis[MAXN];
int ans[MAXN], now[MAXN];
int son[MAXN][MAXN];

struct Edge{
	int a, b;
}e[MAXM];

inline int read(){
	int k = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){k = k * 10 + ch - '0'; ch = getchar();}
	return k * f;
}

inline void add_edge(int a, int b){
	to[cnt] = b; nxt[cnt] = fir[a]; fir[a] = cnt++;
}

void dfs(int u, int f){
	now[++num] = u;
	vis[u] = true;
	for(int i = fir[u]; i != -1; i = nxt[i]){
		int v = to[i];
		if(vis[v]) continue;
		if(e[ban].a == u && e[ban].b == v) continue;
		if(e[ban].a == v && e[ban].b == u) continue;
		son[u][++son[u][0]] = v;
	}
	sort(son[u] + 1, son[u] + son[u][0] + 1);
	for(int i = 1; i <= son[u][0]; i++){
		dfs(son[u][i], u);
	}
}

int main(){
	memset(fir, -1, sizeof(fir)); cnt = 0;
	n = read(), m = read();
	for(int i = 1; i <= m; i++){
		int a = read(), b = read();
		e[i] = (Edge){a, b};
		add_edge(a, b);
		add_edge(b, a);
	}
	
	
	if(m == n - 1){
		dfs(1, 0);
		for(int j = 1; j <= n; j++){
			ans[j] = now[j];
		}
	}
	else{
		ans[1] = 1e9;
		for(int i = 1; i <= n; i++){
			num = 0;
			memset(vis, 0, sizeof(vis));
			ban = i;
			
			for(int j = 1; j <= n; j++) son[j][0] = 0;
			
			dfs(1, 0);
			
			if(num < n) continue;
			
			for(int j = 1; j <= n; j++){
				if(now[j] > ans[j]) break;
				
				if(now[j] < ans[j]){
					for(int k = 1; k <= n; k++){
						ans[k] = now[k];
					}
					break;
				}
			}
		}
	}
	
	for(int i = 1; i <= n; i++){
		printf("%d ", ans[i]);
	}
	
	return 0;
}```
原文地址:https://www.cnblogs.com/asdf1229/p/14548710.html