DP - 狀壓DP - 今日頭條2019筆試 - 畢業旅行問題

DP - 狀壓DP - 今日頭條2019筆試 - 畢業旅行問題

小明目前在做一份畢業旅行的規劃。

打算從北京出發,分別去若干個城市,然後再回到北京,每個城市之間均乘坐高鐵,且每個城市只去一次。

由於經費有限,希望能夠通過合理的路線安排儘可能的省些路上的花銷。

給定一組城市和每對城市之間的火車票的價錢,找到每個城市只訪問一次並返回起點的最小車費花銷。

注意:北京爲1號城市。

輸入格式
城市個數n。

城市間的車票價錢n行n列的矩陣 m[n][n]

輸出格式
輸出一個整數,表示最小車費花銷。

數據範圍
1<n≤20,包括北京
車票價格均不超過1000元。

輸入樣例:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
輸出樣例:
13
說明
共4個城市,城市1和城市1的車費爲0,城市1和城市2之間的車費爲2,城市1和城市3之間的車費爲6,城市1和城市4之間的車費爲5,以此類推。

假設任意兩個城市之間均有單程票可買,且價格均在1000元以內,無需考慮極端情況。


分析:

本題是一道明顯的《最短Hamilton路徑》問題。

Hamilton僅僅最後需要重新回到起點,做一遍Hamilton迴路,最後再從回到起點的路徑中選擇一條最短的即可。

f[i][j]jiink1k0k狀態表示,f[i][j]:當前走到第j個點,且狀態是i的最少花費。\\其中i是一個n位二進制數,若第k位是1,表示已經經過了點k,若是0,表示還未經過點k。

注意:
j本題只要求經過所有點,而不要求經過的順序,因此不好考慮前j個點的狀態。\\比較好的考慮方式是已經經過了哪些點。
[0,n1][0,n1]另外,由於要進行移位運算,狀態的位數是從[0,n-1]的,因此給點標號爲[0,n-1]。
f[1][0]=00100初始化f[1][0]=0,表示狀態的第0位是1,且當前處在0號點的花費是0。

j狀態計算:\\考慮第j個點是從哪個點走過來的。

滿ij1i(1<<j)k1jkf[i][j]=min(f[i(1<<j)][k]+w[k][j])j,k[0,n1]若滿足狀態i的第j位爲1,且狀態i-(1<<j)的第k位爲1,表示當前狀態處在的j點是從k點走過來的,\\則說明狀態能夠發生轉移,f[i][j]=min(f[i-(1<<j)][k]+w[k][j]),j,k∈[0,n-1]。

min(f[(1<<n)1][i])i[0,n1]最終將所有點都經過一次的最小花費就是min(f[(1<<n)-1][i]),i∈[0,n-1]。
0min(f[(1<<n)1][i]+w[i][0])i[1,n1]再回到起點0的最小花費就說min(f[(1<<n)-1][i]+w[i][0]),i∈[1,n-1]。

代碼:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=20,M=1<<N;

int n,f[M][N],w[N][N];

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>w[i][j];
            
    memset(f,0x3f,sizeof f);
    f[1][0]=0;
    
    for(int i=0;i<1<<n;i++)
    for(int j=0;j<n;j++)
        if((i>>j)&1)
        for(int k=0;k<n;k++)
            if((i-(1<<j))>>k&1)
                f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);
                
    int res=1e9;
    for(int i=1;i<n;i++) res=min(f[(1<<n)-1][i]+w[i][0],res);
    
    cout<<res<<endl;
    
    return 0;
    
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章