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