[NOIP2018 提高组] 旅行

题目大意

给定一棵树或基环树,从起点出发,找到一个字典序最小的遍历序列。

题解

可以用 STL set 来存储边,这样字典序排序比较好做。如此,树的部分就直接遍历一遍即可。

考虑基环树的情况,先找到环,然后枚举删除环的每一条边,总复杂度是 $\mathcal{O}(n^2)$ 的。

代码

const int N = 5e4 + 5;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

namespace Main {
	int n, m;
	set <pair<int, int> > e[N];
	int ans[N], tmp[N];
	bool Better() {
		for (int i = 1; i <= n; i++) 
			if (ans[i] != tmp[i]) return ans[i] > tmp[i];
		return 0;
	}
	
	int curId = -1, delId = -1;
	int vis[N];
	void dfs (int u) {
		vis[u] = curId;
		tmp[++tmp[0]] = u;
		if (tmp[0] == n && Better()) memcpy (ans, tmp, sizeof tmp);
		for (auto i = e[u].begin(); i != e[u].end(); ++i) {
			int v = i->first, id = i->second;
			if (vis[v] == curId || id == delId) continue;
			dfs(v);
		}
	}
	
	int cir[N], rho, flag;
	void Circle(int u, int fa) {
		vis[u] = curId;
		for (auto i = e[u].begin(); i != e[u].end(); ++i) {
			int v = i->first, id = i->second;
			if (v == fa || id == cir[1]) continue;
			if (vis[v] == curId) {
				rho = v;
				flag = 1;
				cir[++cir[0]] = id;
				break;
			}
			Circle (v, u);
			if (flag) cir[++cir[0]] = id;
			if (u == rho) flag = 0;
		}
	}
	
	int main () {
		n = Read(), m = Read();
		ans[1] = 1 << 30;
		for (int i = 1; i <= m; i++) {
			int u = Read(), v = Read();
			e[u].insert(make_pair(v, i));
			e[v].insert(make_pair(u, i));
		}
		if (n == m) {
			Circle(1, -1);
			for (int i = 1; i <= cir[0]; i++) {
				delId = cir[i], curId = i, tmp[0] = 0;
				dfs(1);
			}
		} else {
			dfs(1);
		}
		for (int i = 1; i <= n; i++) printf ("%d ", ans[i]);
		putchar(10);
		return 0;
	}
}

int main () {
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	Main::main();
	return 0;
}
EOF

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。

博客信息

作者
Jayun
时间
2023-10-10 10:25:28
博客类型