You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
4.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
using namespace std;
const int N = 1100000, M = N << 1;
typedef pair<int, int> PII;
int res[N], rl; // 欧拉路径数组
int st[N]; // 某条边是不是已经访问过了
int base[10]; // 预处理出10^i次方
PII stk[N]; // 模拟栈,第一维记录节点序号,第二维记录边的边号
int top;
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// 循环版本的dfs求欧拉路径
void dfs() {
// 标识边号为0的边已访问过并且让0号点,0号边进入栈中模拟dfs
st[0] = 1, stk[++top] = make_pair(0, 0);
while (top) { // 如果栈不空,就一直处理,其实就是一个循环版本的dfs
int u = stk[top].first; // 取出栈顶元素
int i;
for (i = h[u]; ~i; i = ne[i]) {
int id = w[i]; // 魔改的边权数组w[i]中记录的是边的真实数值,可以理解为边号
if (st[id]) continue; // 如果这条边已经访问过,就不再搜索
st[id] = 1; // 标识这条边使用
stk[++top] = make_pair(e[i], w[i]); // 点e[i]入栈
h[u] = ne[i]; // 删边,套路,防止回溯后做无用功,能优化一些性能,不写也可以过掉本题,可以提升100MS的性能
break; // 为啥要break呢只有这样玩才能真正的模拟递归如果没有break,就开始横向平铺了,就不是一下跑到底了!
}
// 扫完u了相当于递归完毕
if (i == -1) {
res[++rl] = stk[top].second; // 记录答案
top--; // 出栈
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("POJ1780.in", "r", stdin);
#endif
int n;
// 预处理出10^0=1,10^0=1,10^1=10,10^2=100,10^3=1000...
// 这样一次预处理,下面使用的时候就不用现算了,这比什么快速幂啥的还要靠谱,
base[0] = 1;
for (int i = 1; i <= 6; i++) base[i] = base[i - 1] * 10;
while (scanf("%d", &n) && n) {
// n=1时需要特判此时是一个孤立的点不存在欧拉回路直接输出答案就完了
if (n == 1) {
puts("0123456789");
continue;
}
// 初始化链式前向星
memset(h, -1, sizeof h);
// 初始化边是不是访问过
memset(st, 0, sizeof st);
top = idx = rl = 0; // 栈清空,链式前向星的游标回零,结果数组的游标回零
// 枚举每个数字,也就是每个节点,0~10^(n-1)-1,比如n=3则0~999
for (int i = 0; i < base[n - 1]; i++) {
// 正向思考现在我在i点处通过向我的尾部不断加入0~9会去向哪个节点呢
// 答:需要把你的最高位裁掉,然后,把新添加的数字放在尾部上
// 办法就是对base[n - 2]取模就去掉了最高位然后乘10就是左移然后留出来了最后一位再想办法拼接0~9
int t = i % base[n - 2] * 10;
// 因为使用了链式前向星,而且,本题要求输出字典序最小,所以大数在前,小数在后
// 从i点出发目标点是费劲拼出来的t+j,那么边是啥呢边可和目标点不是一个意思它不能去掉首位应该是i*10+j
for (int j = 9; j >= 0; j--)
add(i, t + j, i * 10 + j);
}
// 开始寻找欧拉回路
dfs();
for (int i = 1; i < n; i++) printf("0"); // 因为是一个一个下场的最开始前面需要补0
for (int i = rl; i; i--) printf("%d", res[i] % 10); // 参考前两题的解释
puts(""); // 一组数据结束,需要换行
}
return 0;
}