思想 :
類TSP問題 首先必須從起點0 出發 以n點爲終點
在DP時 要把起點終點去掉 (否則會把“從k點到n點再到i點”等路徑算進去
所以後面n-=2;其次 每個點經過且僅經過一次 所以用二進制 表示首尾之間的點是否走過
0爲沒走過 1爲走過
方程 f[s][i] 表示從0出發經過集合s中的點到i的最短時間
f[s,i]=min(f[s,i],f[s-i,j]+dis[j,i]);
邊界條件 f[1<<0,i]=輸入中給的從0到i的時間
最後ans=min(ans,f[s][i]+dis[i][終點]);
s表示除起點終點以外的點的集合 i爲其子集
#include <iostream>
#include <cstring>
using namespace std;
int f[(1<<14)][111];
int n,dis[36][36],ans=999999;
int main()
{
memset(f,1,sizeof(f));
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>dis[i][j];
n-=2; //n-=2
//因爲題目要求從起點0出發,且以n爲終點 遍歷所有點
//所以n先減去一個起點 即把二進制表示中的第一個點從“表示起點0”變爲“表示點1” (因爲0必須走 所以刪去二進制表示的起點0 防止重複更新
///因爲終點n也要去掉 所以n-=2;
for(int i=0;i<n;i++) //若n只減去1 此處for 的 i<n-1 保證 不用到終點
f[(1<<i)][i]=dis[0][i+1];//這裏 i=0時 表示點1 而非起點0 i=1 爲點2.。。。
for(int s=0;s<(1<<n);s++) //(1<<N 二進制表示 集合 )枚舉集合 一直枚舉到 除起點 終點以外的所有的點
for(int i=0;i<n;i++)
if(s&(1<<i)) //i在集合中
{
for(int j=0;j<n;j++)
{
if(i==j) continue;
if(s&(1<<j)) //j在集合中
f[s][i]=min(f[s][i],f[s^(1<<i)][j]+dis[j+1][i+1]);//注意加1
}
}
int s=(1<<n)-1;
for(int i=0;i<n;i++)
{
ans=min(ans,f[s][i]+dis[i+1][n+1]);
}
cout<<ans<<endl;
}
http://blog.csdn.net/qq_18455665/article/details/50429230#comments