#1182 : 欧拉路·三(有向图的欧拉路)
#1182 : 歐拉路·三
時間限制:10000ms 單點時限:1000ms 內存限制:256MB描述
小Hi和小Ho破解了一道又一道難題,終于來到了最后一關。只要打開眼前的寶箱就可以通關這個游戲了。
寶箱被一種奇怪的機關鎖住:
這個機關是一個圓環,一共有2^N個區域,每個區域都可以改變顏色,在黑白兩種顏色之間切換。
小Ho控制主角在周圍探索了一下,果然又發現了一個紙片:
機關黑色的部分表示為1,白色的部分表示為0,逆時針連續N個區域表示一個二進制數。打開機關的條件是合理調整圓環黑白兩種顏色的分布,使得機關能夠表示0~2^N-1所有的數字。
我嘗試了很多次,終究沒有辦法打開,只得在此寫下機關破解之法。
——By 無名的冒險者
小Ho:這什么意思啊?
小Hi:我給你舉個例子,假如N=3,我們通過順時針轉動,可以使得正下方的3個區域表示為:
因為黑色表示為1,白色表示為0。則上面三個狀態分別對應了二進制(001),(010),(101)
每轉動一個區域,可以得到一個新的數字。一共可以轉動2N次,也就是2N個數字。我們要調整黑白區域的位置,使得這2^N個數字恰好是0~2 ^N-1
小Ho:我懂了。若N=2,則將環上的黑白色塊調整為"黑黑白白",對應了"1100"。依次是"11",“10”,“00”,"01"四個數字,正好是0~3。那么這個"黑黑白白"就可以打開機關了咯?
小Hi:我想應該是的。
小Ho:好像不是很難的樣子,我來試試!
提示:有向圖歐拉回路
< 十分鐘后 >
小Ho:啊啊啊啊啊,搞不定啊!
小Hi:怎么又是這樣,你怎么做的?
小Ho:是這樣的,每次轉動一個區域不是相當于原來數字去掉最左邊一位,并在最后加上1或者0么。于是我考慮對于"XYYY",它轉動之后可以變成"YYY0"或者"YYY1"。我就將所有的數字0~2^N-1看作 2^N個點,連接所有的(“XYYY”,“YYY0”),(“XYYY”,“YYY1”)。比如當N=3時,我得到了這樣一個圖:
我要做的就是找一條路徑,從一個點出發,走過所有的點后,再回到起點。但是我發現好像很難的樣子。
小Hi:那當然了。你這樣構造出來的路徑叫做哈密頓回路,不是那么容易可以求解的。
小Ho:哎??那我應該怎么做。
小Hi:其實你的想法是沒問題的,但是需要進行一下變換。在你的構圖中我們是用點來表示數字,所以需要經過每一個點。如果我們用邊來表示每一個數字呢?
小Ho:怎么用邊表示數字?
小Hi:其實也很簡單,比如說數字"10011",分別刪掉它第一個數字和最后一個數字,得到"1001",“0011”。然后我們連接一條從"1001"到"0011"的有向邊,表示數字"10011"。則我們可以得到構圖的方法:
對于N,我們構造一個包含2^(N-1)個點和 2^N 條邊的圖,點的編號從0到2^(N-1)-1。編號為i的點表示數字i。對于任意兩個點,如果點i,點j滿足點i的后n-2個數字和點j的前n-2個數字相同,則我們連接有向邊(i,j)。而邊(i,j)表示了數字((i << 1)+(j & 1))。比如對于N=3的時候,我們可以得到:
可以很容易證明對于任意不同邊(i,j),其表示的數字一定不同。
小Ho:這樣構圖話,只要找到一條歐拉回路就可以了。但是一定會有歐拉回路么?
小Hi:當然能了,對于有向圖,其存在歐拉路的條件是,至多有兩個點的入度不等于出度,且這兩個點滿足:其中一個點入度比出度多1,另一個點出度比入度多1。
若所有點的入度都等于出度,則一定存在歐拉回路。這可以通過和無向圖歐拉路同樣的方法進行構造證明。
而我們構造的圖,由構造方法可以知道對于任意一個點,其入度一定為2,出度一定為2。所以它必定存在歐拉回路。
在有向圖中找歐拉路的方法,也仍然可以使用Fleury算法。寫成偽代碼的話:
DFS(u):While (以u為起點,且未被刪除的邊e(u,v))刪除邊e(u,v)DFS(v)EndPathSize ← PathSize + 1Path[ PathSize ] ← u但是,有一點要注意,在使用Fleury算法計算有向圖的歐拉路時,我們需要將path[]倒序輸出才能得到正確的路徑。
小Ho:那找到歐拉回路之后呢?
小Hi:找到歐拉回路之后只要對該條歐拉回路進行拼接就可以得到我們目標的圓盤狀態了。
小Ho:好,我大概明白了。我這就來試試!
輸入
第1行:1個正整數,N。1≤N≤15
輸出
第1行:1個長度為2^N的01串,表示一種符合要
求的分布方案
樣例輸入
3樣例輸出
00010111相關知識點:
定理:
思路:
這題很精妙,
整體題意就是我轉一圈,能夠得到0~(2^N - 1).
假設原來是xyz, x,y,z屬于[0,1],轉一下,就是:
xy(0 or 1) 或者(0 or 1)yz。
即:
xyz–>xy(0 or 1),
xyz–>(0 or 1)yz.
根據上述 轉化規則 就可以構建出 點(xyz的十進制)與 點之間的關系,即可以根據點構造出有向圖。
此時問題就是:一次有且一次走遍所有頂點(此時會想到哈密頓通路)
但是哈密頓通路目前沒有很好的方法來求解。
此時精妙之處是把握住:只要轉一圈能夠得到所有0~(2^N - 1)的數字。
此時把頂點(數字)用邊來表示,重新構造有向圖。
之后就轉化成了:走遍所有邊一次且僅一次,即轉化成找歐拉通路。
用邊來表示頂點的規則:
是將 頂點num 的 二進制 最低位 去掉一位得到一個新的數字s,去掉二進制的最高位去掉一位得到e,此時用有向邊:s—>e表示數字num。
num = s<<1 + (e&1)
本身要2^N - 1個頂點,現在就壓縮成 2 ^ (N-1)個頂點, 2 ^ N - 1條邊表示原來的頂點。
構造有向邊,需要反推s,e:
num去掉最低位 : s = num >> 1
num去掉最高位(最高位是1的時候才是需要管的):
因為是0 ~ 2^N-1,N位二進制即可表示需要的數,
所以:t = (1<<(N-1)) - 1
(1先移到num最高位的位置,然后減一,使最高位變成0,其他位變成1,最后num&t就達到了去除最高位的效果)
e = num & t
構造邊表示數字的無向圖,跑一遍歐拉通路,
因為是有向圖,所以最后結果要從(倒數第一位是回到了起點)倒數第二位逆序輸出。(自己領悟:輸出的 是每個二進制的 最低位 )
注意:
本題采用二維數組來標記邊是不行的,1<<15 是 3e4+,
把握住fleury算法,刪除走過的邊(得子圖),能不走橋就不走橋。
鏈式前向星存圖,邊 直接根據 邊編號來 標記刪除即可
AC代碼:
#include <iostream> #include <cstring> using namespace std; const int N = 5e4+5; struct Edge {int to;int nxt; }edge[N]; int head[N>>1],idx; bool vis[N]; int st[N],cnt; void init() {//head[i]:最終記錄與i相連的最后一條邊的編號//idx : 邊起始編號memset(head,0,sizeof(head));idx = 1; } inline void add_edge(int from,int to) {edge[idx].to = to;edge[idx].nxt = head[from];head[from] = idx++; }void dfs(int s) {for(int i = head[s]; i; i = edge[i].nxt){int to = edge[i].to;//cout<<to<<endl;//走過的邊標記(i為邊的編號)if(vis[i]) continue;vis[i] = true;dfs(to);}st[cnt++] = s; } int main() {int n;cin>>n;int up = 1<< n;init();for(int i = 0; i < up; i++){int x = i >> 1;int t = (1<<(n-1))-1;int y = i & t;//cout<<i<<" "<<x<<" "<<y<<endl;add_edge(x,y);}cnt = 0;dfs(0);for(int i = cnt-2; i >= 0; i--){cout<<(st[i]&1);}cout<<endl;return 0; }總結
以上是生活随笔為你收集整理的#1182 : 欧拉路·三(有向图的欧拉路)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: #1181 : 欧拉路·二(无向图的欧拉
- 下一篇: 树的重心