題目名稱:歐拉路·三
題目鏈接:http://hihocoder.com/contest/hiho51/problem/1
描述
小Hi和小Ho破解了一道又一道難題,終於來到了最後一關。只要打開眼前的寶箱就可以通關這個遊戲了。
寶箱被一種奇怪的機關鎖住:
這個機關是一個圓環,一共有2^N個區域,每個區域都可以改變顏色,在黑白兩種顏色之間切換。
小Ho控制主角在周圍探索了一下,果然又發現了一個紙片:
機關黑色的部分表示爲1,白色的部分表示爲0,逆時針連續N個區域表示一個二進制數。打開機關的條件是合理調整圓環黑白兩種顏色的分佈,使得機關能夠表示0~2^N-1所有的數字。 我嘗試了很多次,終究沒有辦法打開,只得在此寫下機關破解之法。 ——By 無名的冒險者
小Ho:這什麼意思啊?
小Hi:我給你舉個例子,假如N=3,我們通過順時針轉動,可以使得正下方的3個區域表示爲:
因爲黑色表示爲1,白色表示爲0。則上面三個狀態分別對應了二進制(001),(010),(101)
每轉動一個區域,可以得到一個新的數字。一共可以轉動2^N次,也就是2^N個數字。我們要調整黑白區域的位置,使得這2^N個數字恰好是0~2^N-1
小Ho:我懂了。若N=2,則將環上的黑白色塊調整爲"黑黑白白",對應了"1100"。依次是"11","10","00","01"四個數字,正好是0~3。那麼這個"黑黑白白"就可以打開機關了咯?
小Hi:我想應該是的。
小Ho:好像不是很難的樣子,我來試試!
輸入
第1行:1個正整數,N。1≤N≤15
輸出
第1行:1個長度爲2^N的01串,表示一種符合要求的分佈方案
3樣例輸出
00010111
提示:有向圖歐拉回路
< 十分鐘後 >
小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) End PathSize ← PathSize + 1 Path[ PathSize ] ← u
但是,有一點要注意,在使用Fleury算法計算有向圖的歐拉路時,我們需要將path[]倒序輸出才能得到正確的路徑。
小Ho:那找到歐拉回路之後呢?
小Hi:找到歐拉回路之後只要對該條歐拉回路進行拼接就可以得到我們目標的圓盤狀態了。
小Ho:好,我大概明白了。我這就來試試!
代碼如下:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int f[20000][2];
int path[40000],cnt=0;
void dfs(int u)
{
int i;
for(int i=0;i<2;i++)
{
int v=f[u][i];
if(v>-1)
{
f[u][i]=-1;
dfs(v);
}
}
path[++cnt]=u;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(f,-1,sizeof(f));
memset(path,0,sizeof(path));
n=1<<(n-1);
for(int i=0;i<n;i++)
{
int j=(i<<1)&(n-1);
f[i][0]=j;
f[i][1]=j+1; //記錄後面是哪個數
}
cnt=0;
dfs(0);
while(--cnt) //倒序輸出
{
printf("%d",path[cnt]&1);
}
printf("\n");
}
return 0;
}