Files
python/TangDou/AcWing/EulerPath/POJ1780.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

93 lines
4.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}