日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 充電吧
[導讀]首先說明一下,此博文來自我在CSDN上看到的一篇哈密頓回路(有向圖中)的位運算算法,出自GDTZX大神之手,(侵刪),雖然剛從校園畢業(yè),但腦子已經(jīng)完全僵住了,花了許久才看懂了這個算法。 哈密頓回路,

首先說明一下,此博文來自我在CSDN上看到的一篇哈密頓回路(有向圖中)的位運算算法,出自GDTZX大神之手,(侵刪),雖然剛從校園畢業(yè),但腦子已經(jīng)完全僵住了,花了許久才看懂了這個算法。

哈密頓回路,具體到本題之中即從某一個點開始經(jīng)過所有的點一次后再回到該點的不同路徑數(shù)。對于這個不同需要注意兩點:

如果我們將路徑經(jīng)過的點按順序?qū)懴拢热绠攏=3時,若存在123和231。此時,我們認為這兩條路徑是同一條哈密頓回路。而123和213則是不同的哈密頓回路。

若兩個點之間有多條邊,經(jīng)過不同的邊的路徑仍然看作同一條哈密頓回路。不同哈密頓回路只和經(jīng)過的點有關(guān)。因此對于多條邊的情況我們可以將其合并為一條邊來考慮。

對于哈密頓回路,一個簡單的想法就是枚舉所有可能的路徑,判定這個路徑是否存在。即時間復雜度為O(n!)。而題目給定的數(shù)據(jù)范圍為:n <= 12,所以最大可能的枚舉次數(shù)為12! = 479,001,600。

極限的數(shù)據(jù)不到5億,所以我們可以考慮使用暴力來枚舉所有的哈密頓回路。直接采用DFS枚舉我們的每一步,最后判定是否走回到起點。

偽代碼如下:

DFS(int nowVertex, bool visitedVertex, int path, int length)
    visitedVertex[ nowVertex ] = True;
    If (all Vertex is visited) Then
        Count = Count + 1
    Else 
        For (u is next vertex of nowVertex)
            If (not visitedVertex[ u ]) Then
                path[ length ] = u
                DFS(u, visitedVertex, path, length + 1)
            End If
        End For    
    End If
    visitedVertex[ nowVertex ] = False

由于哈密頓回路始終會經(jīng)過每一個點,所以我們只以1為起點就一定可以找出所有的哈密頓回路。

那么這樣是否能夠解決這道題目呢?我只能說不一定能夠解決。

雖然偽代碼相同,但是根據(jù)實現(xiàn)的方法會有不同的運行效率,在某些實現(xiàn)方法下就能夠通過所有的數(shù)據(jù)點,在某些實現(xiàn)方法下就會超過時限。

這里我們介紹一種利用位運算的實現(xiàn),能夠使得整個程序的效率提高很多倍。

首先來看看代碼:

const int MAXN = 14;

int edge[ MAXN ];
int p[1 << MAXN];
int cnt;

void dfs(int nowVertex, int unused) {
    if (!unused) {
        cnt += (edge[nowVertex] & 1);
        return ;
    }
    int rest = unused & edge[ nowVertex ];
    while (rest) {
        int tp = rest & (-rest);
        dfs(p[ tp ], unused - tp);
        rest -= tp;
    }
    return ;
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; ++i)
        p[ 1 << i ] = i + 1;
    while (m--) {
        int u, v;
        scanf("%d %d", &u, &v);
        edge[u] |= 1 << (v - 1);
    }
    dfs(1, (1 << n) - 2);
    printf("%dn", cnt);
    return 0;
}

我們一個一個來解釋每一個變量的含義:

edge[i]表示點i的next節(jié)點情況,我們用二進制表示edge[i],比如當edge[i]=01011時:

+---+---+---+---+---+
| 5 | 4 | 3 | 2 | 1 | 右起第j位
+---+---+---+---+---+
| 0 | 1 | 0 | 1 | 1 | 二進制位的值
+---+---+---+---+---+

從右起第j位,若為1,則表示存在i到j(luò)的邊;若為0,則表示不存在i到j(luò)的邊。所以edge[i]=01011就表示節(jié)點i可以到達節(jié)點1,2,4。

p[i]是為了方便查找點的編號。在edge[i]中若存在01000,我們可以根據(jù)01000=8, 而p[8]=4來快速的通過二進制位來定位節(jié)點編號。

所以有初始化:

for (int i = 0; i < n; ++i)
    p[ 1 << i ] = i + 1;

而通過節(jié)點編號來找到二進制位,則可以直接使用1 << (i - 1)實現(xiàn)。

我們在讀入數(shù)據(jù)時,通過位運算邊可以記錄下所有點之間的連邊情況:

while (m--) {
    int u, v;
    scanf("%d %d", &u, &v);
    edge[u] |= 1 << (v - 1);    // 記錄u有后繼節(jié)點v
}

unused該二進制數(shù)表示我們尚未訪問的節(jié)點集合,同樣的右起第i位表示節(jié)點i,比如unused = 01100

+---+---+---+---+---+
| 5 | 4 | 3 | 2 | 1 | 右起第i位
+---+---+---+---+---+
| 0 | 1 | 1 | 0 | 0 | 二進制位的值
+---+---+---+---+---+

表示我們現(xiàn)在深度優(yōu)先搜索已經(jīng)經(jīng)過了節(jié)點1,2,5,而節(jié)點3,4還尚未經(jīng)過。

由于我們是以節(jié)點1作為起點,初始化的unused也就要去掉最右邊的1,所以代碼為dfs(1, (1 << n) - 2)。

接下來我們逐行解釋dfs函數(shù):

if (!unused) {
    cnt += (edge[nowVertex] & 1);
    return ;
}

當我們所有的節(jié)點都經(jīng)過一次之后,unused中一定不存在1,因此有!unused = true。但是此時并不一定找到了哈密頓回路,我們還需要判斷當前節(jié)點是否能回到起點,也就是節(jié)點1。若nowVertex可以到達節(jié)點1,則edge[nowVertex]最右邊1位一定是1,那么就一定有edge[nowVertex] & 1 = 1

int rest = unused & edge[ nowVertex ];

rest表示當前節(jié)點還可以走到的未訪問節(jié)點。由于&運算的性質(zhì),只有當unusededge[ nowVertex ]對應(yīng)二進制位同時為1時,rest對應(yīng)的二進制位才會為1。其含義就是該節(jié)點尚未訪問,且節(jié)點nowVertex可以到達該節(jié)點。

while (rest) {
    int tp = rest & (-rest);
    dfs(p[ tp ], unused - tp);
    rest -= tp;
}

該循環(huán)的作用是枚舉每一個可以到達的點。

int tp = rest & (-rest);

這里利用了一個性質(zhì),即p & -p等于取出p的最右邊的一個1。舉個例子p=10110:

   +---+---+---+---+---+
p  | 1 | 0 | 1 | 1 | 0 | 
   +---+---+---+---+---+
-p | 0 | 1 | 0 | 1 | 0 | 
   +---+---+---+---+---+
&  | 0 | 0 | 0 | 1 | 0 | 
   +---+---+---+---+---+

我們不斷的利用這個性質(zhì)從rest里面取出可以使用的二進制位,進行dfs(p[ tp ], unused - tp);。同時再枚舉完一個節(jié)點后,將其從rest中刪除,即rest -= tp;。

最后我們再使用printf("%dn", cnt);來輸出我們找到的方案數(shù)。


在上面DFS的基礎(chǔ)上,我們還可以進一步優(yōu)化。

遞歸的過程中,unused很有可能會出現(xiàn)重復的情況,比如說從1->3->2->4和從1->2->3->4,對于dfs(4, unused)來說,此時的unused值都是相同的。因此我們可以采用記憶化搜索的方式進一步降低復雜度。

定義數(shù)組f[n][unused]表示當前節(jié)點為n,且節(jié)點訪問情況為unused時的方案數(shù)量。

那么有:

f[n][unused] = sigma(f[p][unused + (1 << (n - 1))] | (unused & (1 << (n - 1)) == 0) and (p != n) and (edge[p] & (1 << (n - 1)) != 0))

這對應(yīng)的是原dfs函數(shù)中下面這段代碼的逆過程。

while (rest) {
    int tp = rest & (-rest);
    dfs(p[ tp ], unused - tp);
    rest -= tp;
}

三個條件分別為:

(unused & (1 << (n - 1)) == 0)表示當前狀態(tài)中右起第n位為0(p != n)表示前驅(qū)結(jié)點不等于n(edge[p] & (1 << (n - 1)) != 0)表示節(jié)點p能夠到達節(jié)點n

在計算f[n][unused]的過程中,假設(shè)unused的二進制表示中有i個1,則我們需要事先計算出所有i+1個1的狀態(tài)才能夠保證unused + (1 << (p - 1))是正確的結(jié)果。

因此我們在枚舉過程中,需要按照狀態(tài)中1的個數(shù)來枚舉。

其偽代碼:

For numberOfOnes = n-1 .. 0
    For (the number of ones of unused equals numberOfOnes)
        For nowVertex = 1 .. n
            For prevVertex = 1 .. n
                If (unused & (1 << (nowVertex - 1)) == 0) and (prevVertex != nowVertex) and (edge[ prevVertex ] & (1 << (nowVertex - 1)) != 0) Then
                    f[ nowVertex ][ unused ] = f[ nowVertex ][ unused ] + f[ prevVertex ][unused + (1 << (nowVertex - 1))]
                End If
            End For
        End For
    End For
End For

對于f[n][ unused ]數(shù)組,其初始條件為f[1][(1 << n) - 2] = 1

最后需要將所有的f[n][0]中能夠到達節(jié)點1的節(jié)點n累加起來,即可得到所有的方案數(shù)。該算法的理論時間復雜度為O(2^n*n^2)。

結(jié)果分析

該題目一共只有7名選手通過,大部分提交過該題的選手也都有一定的部分分值。

本題直接采用搜索就能夠通過,拉開差距的主要原因是對于偽代碼實現(xiàn)方式的不同,而導致通過的測試點數(shù)量不同。

另外還有一個易錯點是走完所有節(jié)點之后一定要判斷是否可以回到起點。




本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉