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;
	
}

posted @ 2020-08-21 21:01  ·Iris  阅读(273)  评论(0编辑  收藏  举报