這道題是到最基礎的狀壓dp,外加一個floyid距離問題(最短距離).
大概題意是有個人從1號位置出發,經過其他n-1個位置又回到1號位置,並且要求其他的地方至少都要走一遍,告訴有m條路徑,每條路徑都有個起點和終點,還有走這條路徑需要的花費,最後求走完所有的地方花費最少是多少?
下邊來看代碼:
#include<iostream>
#include<cstdio>
using namespace std;
#include<cstring>
int a[18][18],dp[18][1<<18];
const int inf=0x3f3f3f3f;
int n,m;
void floyid()//floyid距離,最短距離的求法
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
int u,v,t;
for(int i=1;i<=n;i++)//對距離初始化
{
for(int j=1;j<=n;j++)
a[i][j]=(i==j)?0:inf;
}
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&t);
if(t<a[u][v])a[u][v]=a[v][u]=t;//這裏很容易漏掉,因爲觀察到題目中n和m的範圍才發現,這是個陷阱,1<=n<=16,1<=m<=100000,這裏的m太大了,也就是路勁數出奇的多,假設所有的地點都用上,路徑數最多也就是16*16,不會有10w,所以一定有一些兩點之間的路徑重複給出,所以要取其中最小的哦。。。
}
if(n==1){cout<<0<<endl;continue;}
floyid();
memset(dp,inf,sizeof dp);
dp[1][1]=0;
for(int s=1;s<(1<<n);s++)
{
for(int i=1;i<=n;i++)
if(s&(1<<i-1))continue;//狀態轉移的時候,需要這個被轉移的地點還沒有走過,如果走過了,那麼直接就是pass掉了
else {
for(int k=1;k<=n;k++)
if(s&(1<<k-1)){//要求中間的節點k一定已經走過了,只有這樣纔可以繼續由這個中間節點到達最後的狀態轉移的i節點
dp[i][s|(1<<i-1)]=min(dp[i][s|(1<<i-1)],dp[k][s]+a[k][i]);
}
}
}
// for(int i=1;i<=n;i++)
// {
// cout<<i<<":"<<dp[i][(1<<n)-1]<<endl;
// }
int minn=inf;
for(int i=2;i<=n;i++)//這裏是算加上第i個地點回到1的距離,最後取最小的
minn=min(minn,dp[i][(1<<n)-1]+a[i][1]);
printf("%d\n",minn);
}
}