openjudge4979 - 海賊王之偉大航路 題解

原題連接c++

題目簡要分析

N個點,從1號點到N號點求最短路徑,且每一個點都要遍歷到。如今要你求出最優方案。數組

這道題看到後,首先的想法莫過於搜索、暴力了。這顯然不太可能。而進一步思考,使用Floyed和Dijkstra也不太好用,由於題目描述說:"每一個點都要遍歷到",天然又否決了這個方法。那麼怎麼辦呢?spa

狀態壓縮DP


什麼是狀態壓縮?code

因爲全部點在DP階段中的狀態只有走過( true )和沒走過( false ),那麼用0、1的二進制表示每一個階段便可。blog

好比如今有5個點,如今只通過了二、三、5號點。那麼二進制就能夠這樣表示:get

01101

再轉換成十進制便可it


如何記錄階段?搜索

因此咱們的DP數組須要二維,一維記錄當前二進制狀態用十進制表示的數,另外一維記錄當前階段點,也就是當前點是否通過。遍歷

dp[i][j] 表示i狀態下走到j號點最優方案

狀態轉移方程是什麼?二進制

咱們只要再枚舉一個點,即枚舉上一個點,合法就取min值便可。

AC代碼

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 20 + 5;

int n,dp[(1 << 17)][MAXN];
int dis[MAXN][MAXN];

inline int read(){//快速讀入 
    int f = 1, x = 0;
    char c = getchar();

    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }

    while (c >= '0' && c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }

    return f * x;
}

int Hamilton(){
    memset(dp,0x3f,sizeof(dp));
    dp[1][1] = 0;
    for(int i = 1;i <= (1 << (n + 1)) - 1; i++){
        for(int j = 1;j <= n; j++){
            if(!((i >> j) & 1))continue;
            for(int k = 1;k <= n; k++){
                if(!((i >> k) & 1))continue;
                dp[i][j] = min(dp[i][j],dp[i ^ (1 << j)][k] + dis[k][j]);
            }
        }
    }
    return dp[(1 << (n + 1)) - 1][n];
}

int main(){
    n = read();
    for(int i = 1;i <= n; i++)
        for(int j = 1;j <= n; j++){
            dis[i][j] = read();
        }
    
    //dp[i][j]:i表示全部點壓縮後的狀態,j表示當前在的點 
    //則dp[(1 << (n + 1)) - 1][n]就是最後答案
    cout<<Hamilton();
    return 0;
}
相關文章
相關標籤/搜索