题意
给定 n 个标号为 0, 1, 2, ..., n-1 的点.
一只青蛙在点 i 能够跳往 (2 * i) % n 和 (2 * i + 1) % n .
若该青蛙从点 0 出发, 是否能够跳出一条哈密顿回路, 如果可以, 还要找到字典序最大的.
2 <= N <= 10000 .
分析
我们先研究一些小的情形.
暴力打表发现, 当 n 为奇数时, 一定无解.
接下来保证 n 为偶数.
当 n 为偶数时, 2 * (n / 2) = 0 (mod n) .
所以 i 和 i + n / 2 能够跳到的点是相同的, 我们考虑把它们缩点.
以 n = 8 为例, 画出这样一个图.
----------------------------------------
↓ ↑
--------- -------- -------- ---------
←←← | 0 | | 1 | <- | 2 | | 3 | →→→
| | | -> | | -> | | <- | | |
→→→ | 4 | | 5 | | 6 | | 7 | ←←←
--------- ---------- -------- ----------
↓ ↑
----------------------------------------
发现每个点不仅有且仅有两条出边, 而且有且仅有两条入边.
而我们的目标是每个点经过两次, 所以我们的目标相当于是经过所有的边, 直接求欧拉回路.
实现
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 6 const int N = 10005; 7 8 int n, Lis[N], tot; 9 bool v[N]; 10 11 void Dfs(int x) { 12 if (!v[x << 1 | 1]) { 13 v[x << 1 | 1] = true; 14 Dfs((x << 1 | 1) % (n >> 1)); 15 Lis[++tot] = x << 1 | 1; 16 } 17 if (!v[x << 1]) { 18 v[x << 1] = true; 19 Dfs((x << 1) % (n >> 1)); 20 Lis[++tot] = x << 1; 21 } 22 } 23 24 int main(void) { 25 #ifndef ONLINE_JUDGE 26 freopen("frog.in", "r", stdin); 27 #endif 28 29 scanf("%d", &n); 30 if (n & 1) return puts("-1"), 0; 31 32 Dfs(0), Lis[++tot] = 0; 33 while (tot > 0) printf("%d ", Lis[tot--]); puts(""); 34 35 return 0; 36 }