题目传送门:【POJ 1041】
题解在题意下面
题目大意:给你一个无向图,一共有 m 个点,n 条边(1 ≤ m ≤ 44,1 ≤ n ≤ 1995),每个点连接着最多不超过 44 条边,每条边都恰好连接两个点。每个点和每条边都有自己的编号,并且任意两个点/两条边的编号不同。现在,Johnny 想要遍历这个无向图,并且要使每一条边都恰好地被走一次。
输入多组数据,每组数据包含三个整数 x , y , z,表示编号为 x , y 的两个点被一条编号为 z 的边连了起来。每组数据输入结束的标志为 x=y=0。当一组数据输入结束后,在新的一组数据中输入 x=y=0 表示输入结束。Johnny 的起点位于每组数据第一行的 x 和 y 中较小的值所对应的点。
对于每组数据,输出为一行整数,代表 Johnny 遍历这个图的次序。优先输出字典序最小的次序。如果他不能遍历完这个图,输出 “Round trip does not exist.” (不含引号)
输入样例:
1 2 1
2 3 2
3 1 6
1 2 5
2 3 3
3 1 4
0 0
1 2 1
2 3 2
1 3 3
2 4 4
0 0
0 0
输出样例:
1 2 3 5 4 6
Round trip does not exist.
题目分析:
这道题,在我分析之前,我一定要认真地看着它,然后,大声地喊道:wqnmlg******!辣鸡出题人连个题意都不好好写!
本题极为毒瘤,题意“按字典序输出”,却设一个 Special Judge;判断有欧拉回路的条件竟是……只需要判断出入度即可???Excuse me?
回到题意。
由题,除去一系列干扰项,这道题就是求一个欧拉回路,并且只需要随便输出一种遍历次序即可。
所以就有两种方法:dfs,或者是像本题一样用 Fleury 算法求欧拉回路。要判断 Johnny 能否回到原来的位置,只需要看每个点的度数是否为奇数即可(任意一个点是奇数则不能返回,所有的点都不是奇数则可以返回)。之后直接使用 Fleury 算法求出欧拉回路即可。
要注意的是,需要额外开一个栈来保存每次走的边的编号,最后直接输出编号即可。
如果想要了解 Fleury 算法的核心,可以参考这篇文章:Fleury(弗罗莱)算法求欧拉路径
下面附上代码:
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<stack>
- using namespace std;
- const int MX=80005;
- struct Edge{
- int to,next,num;
- }edge[MX*2];
- int head[MX],now=1,ss,deg[MX]; //初始化为 1,实际从 2 开始,方便之后异或
- bool vis[MX*2];
- stack<int> s,eg;
- inline void adde(int u,int v,int no){
- edge[++now].to=v;
- edge[now].num=no;
- edge[now].next=head[u];
- head[u]=now;
- }
- void dfs(int u){
- s.push(u);
- for (int i=head[u];i>1;i=edge[i].next){
- if (!vis[i]){ //判断每一条边是否经过
- vis[i]=vis[i^1]=true;
- eg.push(edge[i].num);
- dfs(edge[i].to);
- break;
- }
- }
- }
- void fleury(int u){
- s.push(u);
- while (!s.empty()){
- bool can_go=false;
- int u=s.top();
- s.pop(); //此时出栈,方便在dfs中将点加入栈
- for (int i=head[u];i>1;i=edge[i].next){
- if (!vis[i]){ //有路可走
- can_go=true;
- break;
- }
- }
- if (!can_go){
- if (!eg.empty()) printf(“%d ”,eg.top());
- eg.pop();
- }
- else dfs(u);
- }
- }
- void _init(){
- memset(deg,0,sizeof(deg));
- memset(edge,0,sizeof(edge));
- memset(head,0,sizeof(head));
- memset(vis,0,sizeof(vis));
- while (!s.empty()) s.pop();
- while (!eg.empty()) eg.pop();
- now=1;
- }
- int main(){
- int a,b,num; //a,b:两个点的编号 num:边的编号
- num=0;
- while (1){
- bool ok=true;
- scanf(”%d%d”,&a,&b);
- if (a==0 && b==0) break;
- _init();
- ss=min(a,b); //第一行较小的那个就是Johnny住的地方
- scanf(”%d”,&num);
- adde(a,b,num),adde(b,a,num);
- ++deg[a],++deg[b];
- while (1){
- scanf(”%d%d”,&a,&b);
- if (a==0 && b==0) break;
- scanf(”%d”,&num);
- adde(a,b,num),adde(b,a,num);
- ++deg[a],++deg[b];
- }
- for (int i=1;i<=44;i++){ //判断每个点的度???? (总共只有44个点)
- if (deg[i]&1){
- printf(”Round trip does not exist.\n”);
- ok=false;
- break;
- }
- }
- if (!ok) continue;
- fleury(ss);
- printf(”\n”);
- }
- return 0;
- }