「NOIP 2018」 旅行

给出一个 \(n\) 个点, \(m\) 条边的无向联通图,可以从任意一个点出发,前往一个相邻未访问过的结点,或者沿第一次访问当前结点的边返回。要求找出一个访问方案,使得每个结点都被访问过,且方案的字典序最小。(\(m=n-1\text{ 或 }m=n\))

Luogu

分析

\(m=n-1\) 时,显然是一棵树,很容易想到用贪心,从 \(1\) 号结点开始访问,每次去往一个未访问过的数字最小的结点。所以我们直接将每个点的相邻结点排序,然后暴搜就好了。

\(m=n\) 时,图是一棵基环树。这时用之前的策略,很容易找到反例。于是我们手推样例,发现在环上一定有一条边是不会被经过的,所以我们可以找到这条边,将其删去,就可以继续按照 \(m=n-1\) 的做法做了。

具体如下:

首先找环。假设当前结点为 \(u\) ,它的一个相邻结点为 \(v\) ,如果 \(v\) 已经被访问过了,而且还没有找到一个在环上的结点,那么 \(u\)\(v\) 一定在环上,这时,我们将 \(v\) 标记为第一个环上的结点。而后回溯时,如果子节点是环上的点,那么当前结点也会是环上的点,但 \(v\) 的父亲结点除外。

找到所有的环上的点后,我们再找到环上的边,把它们储存下来。然后暴力枚举删去哪条边,按照 \(m=n-1\) 的做法求出方案,然后比较,取字典序最小的方案。

这个方法可谓是相当的暴力......事实证明,它跑的确实也很慢.......

代码

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 5001
#define il inline
#define re register
#define INF 0x3f3f3f3f
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;

template <typename T> inline void read(T &x) {
	T f = 1; x = 0; char c;
    for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    x *= f;
}

struct edge {
	int to, nxt;
} e[N<<1];

struct huan {
	int u, v;
} h[N];

int n, m, tot, c, lst;
int head[N<<1],cnt;
int ans[N], ret[N];
bool inh[N], vis[N];

vector <int> g[N];

void insert(int u, int v) { e[++cnt].to = v, e[cnt].nxt = head[u], head[u] = cnt; }

void dfs1(int u, int fa) {
	ans[++c] = u; vis[u] = 1;
	for (int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if (!vis[v]) dfs1(v, u);
	}
}

void find(int u, int fa) {
	vis[u] = 1;
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (v == fa) continue;
		if (vis[v] && lst) continue;
		if (vis[v] && !lst) { inh[u] = 1, lst = v; continue; }
		find(v, u);
		if (inh[v] && v != lst) inh[u] = 1;
	}
}

void add(int u, int fa) {
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (v == fa) continue;
		if (inh[v]) {
			h[++tot] = (huan){u, v};
			if (v == lst) return;
			add(v, u);
			return;
		}
	}
}

void dfs2(int u, int fa, int k) {
	ret[++c] = u, vis[u] = 1;
	int a = h[k].u, b = h[k].v;
	for (int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if (!vis[v]) {
			if ((u == a && v == b) || (u == b && v == a)) continue;
			dfs2(v, u, k);
		}
	}
}

void cmp() {
	for (int i = 1; i <= n; ++i)
		if (ans[i] > ret[i]) {
			memcpy(ans, ret, sizeof ret);
			break;
		}
		else if (ans[i] < ret[i]) break;
}

int main() {
	int u, v;
	read(n), read(m);
	for (int i = 1; i <= m; ++i) {
		read(u), read(v);
		insert(u, v), insert(v, u);
		g[u].push_back(v), g[v].push_back(u);
	}
	for (int i = 1; i <= n; ++i) sort(g[i].begin(), g[i].end());
	if (m == n - 1) dfs1(1, 0);
	else {
		find(1, 0);
		add(lst, 0);
		memset(ans, INF, sizeof ans);
		for (int i = 1; i <= tot; ++i) {
			memset(vis, 0, sizeof vis);
			c = 0;
			dfs2(1, 0, i);
			cmp();
		}
	}
	for (int i = 1; i <= n; ++i) printf("%d ", ans[i]);
	return 0;
}
posted @ 2020-02-04 18:59  小蒟蒻hlw  阅读(191)  评论(0)    收藏  举报