「LuoguP1341」 無序字母對(歐拉回路
題目描述
給定n個各不相同的無序字母對(區分大小寫,無序即字母對中的兩個字母可以位置顛倒)。請構造一個有n+1個字母的字符串使得每個字母對都在這個字符串中出現。
輸入輸出格式
輸入格式:第一行輸入一個正整數n。
以下n行每行兩個字母,表示這兩個字母需要相鄰。
輸出格式:輸出滿足要求的字符串。
如果沒有滿足要求的字符串,請輸出“No Solution”。
如果有多種方案,請輸出前面的字母的ASCII編碼盡可能小的(字典序最小)的方案
輸入輸出樣例
輸入樣例#1: 復制4 aZ tZ Xt aX
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」 無序字母對(歐拉回路