1. 程式人生 > >「LuoguP1341」 無序字母對(歐拉回路

「LuoguP1341」 無序字母對(歐拉回路

.net clas 是否 badge 題目 輸入輸出 freopen 結束 紅色

題目描述

給定n個各不相同的無序字母對(區分大小寫,無序即字母對中的兩個字母可以位置顛倒)。請構造一個有n+1個字母的字符串使得每個字母對都在這個字符串中出現。

輸入輸出格式

輸入格式:

第一行輸入一個正整數n。

以下n行每行兩個字母,表示這兩個字母需要相鄰。

輸出格式:

輸出滿足要求的字符串。

如果沒有滿足要求的字符串,請輸出“No Solution”。

如果有多種方案,請輸出前面的字母的ASCII編碼盡可能小的(字典序最小)的方案

輸入輸出樣例

輸入樣例#1: 復制
4
aZ
tZ
Xt
aX
輸出樣例#1: 復制
XaZtX
 

說明

【數據規模與約定】

不同的無序字母對個數有限,n的規模可以通過計算得到。


題解

首先翻譯一下題面吧。

給定n條無向邊,試構造一條路徑恰好經過每條邊1次。

如果可以構造,輸出途徑的點的編號。

否則輸出No Solution。

其實想明白所謂的字母對只是無向邊的話,這道題就是很清晰的歐拉路徑了。

——以下來自歐拉回路路徑求解 - STILLxjy - CSDN博客——

Hierholzer 算法:
另一種計算歐拉路的算法是 Hierholzer 算法。這種算法是基於這樣的觀察:

技術分享圖片
在手動尋找歐拉路的時候,我們從點 4 開始,一筆劃到達了點 5,形成路徑 4-5-2-3-6-5。此時我們把這條路徑去掉,則剩下三條邊,2-4-1-2 可以一筆畫出。

這兩條路徑在點 2 有交接處(其實點 4 也是一樣的)。那麽我們可以在一筆畫出紅色軌跡到達點 2 的時候,一筆畫出黃色軌跡,再回到點 2,把剩下的紅色軌跡畫完。

由於明顯的出棧入棧過程,這個算法可以用 DFS 來描述。
如果想看得更仔細一點,下面是從點 4 開始到點 5 結束的 DFS 過程,其中 + 代表入棧,- 代表出棧。
4+ 5+ 2+ 3+ 6+ 5+ 5- 6- 3- 1+ 4+ 2+ 2- 4- 1- 2- 5- 4-

我們把所有出棧的記錄連接起來,得到
5-6-3-2-4-1-2-5-4

諸位看官可以自己再選一條路徑嘗試一下。不過需要註意的是,起始點的選擇和 Fleury 要求的一樣。
這個算法明顯要比 Fleury 高效,它不用判斷每條邊是否是一個橋。

然後就套歐拉路徑的板子就好啦。

(實在沒懂怎麽“計算得到”n的規模,好在不用這個條件QAQ

 1 /*
 2     qwerta
 3     P1341 無序字母對
 4     Accepted
 5     100
 6     代碼 C++,1.46KB
 7     提交時間 2018-09-30 11:11:47
 8     耗時/內存
 9     28ms, 1052KB
10 */
11 #include<algorithm>
12 #include<iostream>
13 #include<cstdio>
14 #include<stack>
15 using namespace std;
16 int g[253][253];
17 int d[253];//度數
18 stack<int>st;//這個是記錄棧,不是搜索棧!    
19 void dfs(int x)//dfs找點
20 {
21     for(int j=A;j<=z;++j)//這樣循環就可以保持字典序最小啦
22     if(g[x][j])
23     {
24         g[x][j]--;
25         g[j][x]--;//反向邊也要刪
26         dfs(j);//繼續遞歸
27     }
28     st.push(x);//出棧的時候記錄下來
29     return;
30 }
31 int fa[257];//用並查集維護是否有多個聯通塊
32 int fifa(int x)
33 {
34     if(fa[x]==x)return x;
35     return fa[x]=fifa(fa[x]);
36 }
37 int main()
38 {
39     //freopen("a.in","r",stdin);
40     ios::sync_with_stdio(false);
41     cin.tie(false),cout.tie(false);//關閉同步流(cin伴侶
42     int n;
43     cin>>n;
44     for(int i=A;i<=z;++i)//初始化並查集
45     fa[i]=i;
46     for(int i=1;i<=n;++i)
47     {
48         char x,y;
49         cin>>x>>y;
50         g[x][y]++;
51         g[y][x]++;//臨接矩陣存邊
52         d[x]++;
53         d[y]++;//度數++
54         int u=fifa(x),v=fifa(y);
55         if(u!=v)fa[u]=v;//維護並查集
56     }
57     //判定是否有解
58     int num=0;
59     for(int i=A;i<=z;++i)
60     if(d[i]%2==1)num++;
61     if(num!=0&&num!=2){cout<<"No Solution";return 0;}
62     int tag=0;
63     for(int i=A;i<=z;++i)
64     if(d[i])
65     {
66         if(!tag)tag=i;
67         else if(fifa(tag)!=fifa(i)){cout<<"No Solution";return 0;}
68     }
69     //找是否有奇點
70     int s=-1;
71     for(int i=A;i<=z;++i)
72     if(d[i]%2==1){s=i;break;}
73     if(s==-1)//如果沒有奇點就找AscII最小的點
74       for(int i=A;i<=z;++i)
75       if(d[i]){s=i;break;}
76     dfs(s);//遞歸找點
77     while(!st.empty())
78     {
79         cout<<(char)st.top();
80         st.pop();
81     }//輸出
82     return 0;
83 }

皮一下:

技術分享圖片

「LuoguP1341」 無序字母對(歐拉回路