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回路,最後再從回到起點的路徑中選擇一條最短的即可。
狀態表示,f[i][j]:當前走到第j個點,且狀態是i的最少花費。其中i是一個n位二進制數,若第k位是1,表示已經經過了點k,若是0,表示還未經過點k。
注意:
本題只要求經過所有點,而不要求經過的順序,因此不好考慮前j個點的狀態。比較好的考慮方式是已經經過了哪些點。
另外,由於要進行移位運算,狀態的位數是從[0,n−1]的,因此給點標號爲[0,n−1]。
初始化f[1][0]=0,表示狀態的第0位是1,且當前處在0號點的花費是0。
狀態計算:考慮第j個點是從哪個點走過來的。
若滿足狀態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,n−1]。
再回到起點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;
}