P1171
经典的状压dp问题
题解思路
看到数据范围\(1≤ n ≤ 20\)
我们考虑对已经到达过的村庄进行\(2\)进制状压,用第一维表示。
另外,我们需要知道自己当前处于已经到哪个点,所以将其表示在第二维。
所以得出状态\(f[j][i]\)
接下来的题解叙述中,状态中起点位置我并没有设定,所以会出现邻接矩阵中\(k\)与\(i\)加一得情况
接下来状态转移方程就比较容易设计了
\[f[j][i] = \min\lbrace f[s][k] +a[k+1][i+1]\ \ ,s =j \ \ xor \ \ i ,k\in s\rbrace
\]
该方程表示当前集合为\(j\)且目前处于\(i\)点,由当集合为\(s\)且处于\(k\)点时外加上从\(k\)到\(i\)距离递推而来.
其中\(a[k+1][i+1]\)表示从\(k\)点到\(i\)点.
集合\(s\)为集合\(j\)除去\(i\)点,\(k\)为集合\(s\)中某一点
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define INF 1<<30;
const int minn=(1<<19)+5;
int f[minn][25];
int a[23][23];
signed main()
{
int n;
memset(f,0x3f,sizeof(f));
ios_base::sync_with_stdio(false);
cout.tie(NULL);
cin.tie(NULL);
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
}
n--;
for(int j=1 ;j <= (1<<n) - 1 ; j++ )
{
for(int i=1 ;i<=n;i++)
{
if(((1<<i-1) & j) == 0) continue;
if( (1<<i-1) == j )
{
f[j][i] = a[1][i+1];
break;
}
int s= j ^ (1<<i-1);
for(int k=1;k<=n;k++)
{
if((1<<k-1 & s) == 0) continue;
f[j][i] = min(f[j][i] , f[s][k] + a[k+1][i+1]);
}
}
}
int ans=INF;
for(int i=1;i<=n;i++)
{
ans=min(ans,f[(1<<n)-1][i] + a[i+1][1]);
}
cout<<ans;
}