经典的状压dp问题
题解思路
看到数据范围(1≤ n ≤ 20)
我们考虑对已经到达过的村庄进行(2)进制状压,用第一维表示。
另外,我们需要知道自己当前处于已经到哪个点,所以将其表示在第二维。
所以得出状态(f[j][i])
接下来的题解叙述中,状态中起点位置我并没有设定,所以会出现邻接矩阵中(k)与(i)加一得情况
接下来状态转移方程就比较容易设计了
[f[j][i] = minlbrace f[s][k] +a[k+1][i+1] ,s =j xor i ,kin s
brace
]
该方程表示当前集合为(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;
}