UVA302 John's trip
John有很多朋友住在不同的街,想去拜访每位朋友,同时希望走的路最短。因为道路很窄,John在一条路上不能往回走。
John希望从家出发,拜访完所有的朋友之后回到自己的家,且总路程最短。John意识到如果可以每条路径都走一次,然后返回起点,应该是最短的路径。写一个程序帮助John找到这样的路径。
给出的每条街连接两个路口,最多有1995条街(编号从1到n),44个路口(编号从1到m)。
裸的欧拉回路题,可以不用\(dfs\)。
我们用两个栈,一个用来模拟,一个用来记答案。因为答案要求保证字典序最小的那一组,所以我们先对与每个点相连的点按从小到大排序,这样先选编号小的点,就可以保证找到的第一组答案就是字典序最小的那一组答案了。
小优化:当我们访问完一条边后,把当前选的这个点连的边删除,这样就可以保证每次访问的边都是未访问过的边了,时间复杂度也由\(O(N×M)\)降为了\(O(N+M)\)。
还有最要注意的一点就是每一组解行末无空格。(被卡了好久= =)
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct edge
{
int to,id;
}stk[10000];
int st,d[50],n,ans[10000],num,top,vis[10000],m; //d存每个点的度,如果有度数是奇数的点,那么肯定无欧拉回路.
vector <edge> a[50];
int cmp(edge x,edge y)
{
return x.id<y.id;
}
void euler(int s) //找欧拉回路
{
stk[++top].to=s; //模拟栈
vector <edge>::iterator it;
while (top>0)
{
int u=stk[top].to,w=stk[top].id,fl=0;
for (it=a[u].begin();it!=a[u].end();it++)
{
int v=(*it).to,i=(*it).id;
if (!vis[i]) //vis代替删边
{
fl=1;
stk[++top].to=v;
stk[top].id=i;
vis[i]=1;
break; //进入下一层
}
}
if (!fl) //回溯
{
top--;
ans[++num]=w; //更新答案栈
}
}
}
void clear()
{
for (int i=1;i<=n;i++)
a[i].clear(),d[i]=0,vis[i]=0;
for (int i=1;i<=1995;i++)
vis[i]=0;
num=0;
m=0;
}
int main()
{
int x,y,z;
cin>>x>>y;
while (x||y)
{
clear();
int fl=0;
cin>>z;
n=max(x,y);
st=min(x,y);
m++;
a[x].push_back((edge){y,z});
a[y].push_back((edge){x,z});
d[x]++;
d[y]++;
cin>>x>>y;
while (x||y)
{
cin>>z;
a[x].push_back((edge){y,z});
a[y].push_back((edge){x,z});
n=max(max(x,y),n);
d[x]++;
d[y]++;
m++;
cin>>x>>y;
}
for (int i=1;i<=n;i++)
if (d[i]%2!=0)
{
cout<<"Round trip does not exist."<<endl;
fl=1;
break;
}
if (!fl)
{
for (int i=1;i<=n;i++)
sort(a[i].begin(),a[i].end(),cmp);//排序保证解的字典序最小
euler(st);
for (int i=num-1;i>=2;i--)
cout<<ans[i]<<" ";
cout<<ans[1]<<endl;
}
cin>>x>>y;
cout<<endl;
}
return 0;
}