SGU - 101 Domino(欧拉路径)

思路

题意是给你n个多米诺骨牌,骨牌的两面都有数字,要求你找出一个放置序列使得相邻两个骨牌相对的两面数字相同。注意到如果我们以数字为点(0-7),同时以骨牌建边的话,那么这个问题就是一个欧拉路径问题。欧拉路径是否存在可以用是否所有点度数都是偶数或者图中是否仅有两个奇数度的点这种方法来判定,注意本题的No solution还有图不连通的可能性,要特别判断。在完成欧拉路径是否存在的判定后,我们考虑如何输出路径。我们不能dfs暴力尝试,这样的复杂度显然是不能接受的。看了题解后学习到了一个全新的fleury算法,时间复杂度O(m),即将所有边遍历一次。
hihocoder上的伪代码,如果有奇数度的点,我们从奇数度的点开始dfs,否则任取一个出现过的点。

1
2
3
4
5
6
7
DFS(u):
While (u存在未被删除的边e(u,v))
删除边e(u,v)
DFS(v)
End
PathSize ← PathSize + 1
Path[ PathSize ] ← u

这样做为什么是正确的?想象一下我们dfs第一次终止的时候是什么样的,当有两个奇数度的点的时候,这样一条链显然是从一个奇数度点到另外一个奇数度的点(偶数时候可以随便取两个点,本质上其实是这个链形成了一个环),而其他的点可以看作一个一个在链上的环(因为我们已经知道了图中一定有一个欧拉路径)。知道了这个我们在dfs回退时记录一下边(见伪代码),就相当于从链回退,进入环,退出环,继续沿链回退的过程,最终我们得到的就是该图的欧拉路径。

参考

hihocoder-欧拉路·二

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INTINF 0x7fffffff
#define LLINF 0x7fffffffffffffff
#define ms(a,val) memset((a),(val),(sizeof(a)))
#define sqr(x) ((x)*(x))
#define all(x) (x).begin(),(x).end()
using namespace std;
ll quickpow(ll a,ll b,ll MOD){ll ans=1;a%=MOD;while(b){if(b&1ll)ans=ans*a%MOD;a=a*a%MOD,b>>=1;}return ans;}
ll gcd(ll a,ll b){return a%b==0?b:gcd(b,a%b);}
//head

const int MAXN=10,MAXM=1005;
int cnt[MAXN];
int head[MAXN],nxt[MAXM],to[MAXM],vis[MAXM],dir[MAXM],id[MAXM],edgecnt;
int sta[MAXM][2],total;
void addedge(int u,int v,int w,int tid){
to[edgecnt]=v;
dir[edgecnt]=w;
id[edgecnt]=tid;
nxt[edgecnt]=head[u];
head[u]=edgecnt++;
}
void fleury(int x){
for(int i=head[x];~i;i=nxt[i]){
if(!vis[i]){
vis[i]=vis[i^1]=1;
fleury(to[i]);
sta[total][0]=id[i];
sta[total++][1]=dir[i];
}
}
}

int main() {
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
#endif // ONLINE_JUDGE
int n;
while(~scanf("%d",&n)){
int u,v;
ms(cnt,0),ms(vis,0),ms(head,-1);
edgecnt=0,total=0;
for(int i=0;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v,0,i+1);
addedge(v,u,1,i+1);
cnt[u]++,cnt[v]++;
}
int sum=0;
for(int i=0;i<=6;i++){
if(cnt[i]%2==1){
sum++;
}
}
if(sum==0||sum==2){
for(int i=0;i<=6;i++){
if((!sum&&cnt[i])||(sum==2&&cnt[i]%2==1)){
fleury(i);
break;
}
}
if(total!=n){
printf("No solution\n");
}
else{
for(int i=n-1;i>=0;i--){
printf("%d %c\n",sta[i][0],sta[i][1]?'-':'+');
}
}
}
else{
printf("No solution\n");
}
}
return 0;
}