日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

#1182 : 欧拉路·三(有向图的欧拉路)

發(fā)布時(shí)間:2024/9/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 #1182 : 欧拉路·三(有向图的欧拉路) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

#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):

  • 哈密頓通路:經(jīng)過圖(有向圖和無向圖)中** 所有頂點(diǎn) 一次且僅一次 的 通路**稱作哈密頓通路。
  • 。。。回路。。哈密頓回路,哈密頓回路也稱哈密頓圖。
  • 。。。只有通路沒有回路稱半哈密頓圖。
  • 平凡圖是哈密頓圖。
    定理:
  • 定理1:有向圖歐拉圖 當(dāng)且僅當(dāng)它是強(qiáng)連通且 每一個(gè)頂點(diǎn)的 入度 等于 出度
  • 定理2:有向圖半歐拉圖 當(dāng)且僅當(dāng)它是單向連通的 **且 恰有兩個(gè)奇度頂點(diǎn)其中一個(gè)頂點(diǎn)的入度比出度大1,另一個(gè)頂點(diǎn)的出度比入度大1,其余頂點(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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。