旅行商問題
求解從一點出發經過其它各點僅一次並回到出發點的最短路徑
當n的個數不到時,可通過狀壓dp求解
狀態壓縮
將每個點是否訪問過編碼為0或1,那么當n=4時,訪問所有點后的狀態為1111,初始時狀態為0001。
達到每個狀態的最后一步可能是從第1,2,3,4個位置轉移過來的,因此我們需要記錄達到當前狀態時,上次訪問的點
由此我們可以寫出狀態轉移方程
dp[t][j]=min(dp[t][j],dp[s][i]+dis[i][j])
由狀態轉移方程可知我們需要枚舉狀態s,轉移到狀態t的上一步j,以及狀態s的上一步i
#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int dis[22][22];
//dp[i][j]表示已訪問狀態為i,上次訪問點為j的最小距離
int main()
{
int n;
scanf("%d",&n);
vector<vector<int>> dp(1<<(n),vector<int>(n+1,INF));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&dis[i][j]);
}
}
dp[1][1]=0;
//枚舉狀態s,更新從s經過i到下一狀態t的最短路徑
for(int s=1;s<(1<<n);s++){
for(int i=1;i<=n;i++){
if(s&(1<<(i-1))){
for(int j=1;j<=n;j++){
if(!(s&(1<<(j-1)))){
int t=(s|(1<<(j-1))); //新狀態
//cout<<i<<" "<<j<<" "<<s<<" "<<t<<endl;
dp[t][j]=min(dp[t][j],dp[s][i]+dis[i][j]);
}
}
}
}
}
int ans=INF;
for(int i=2;i<=n;i++){
ans=min(ans,dp[(1<<n)-1][i]+dis[i][1]);
}
printf("%d\n",ans);
return 0;
}