大家应该都知道最小生成树问题吧

那么斯坦纳树问题就是在一张图上固定k个点,求一个最优子图使这k个点连通(其他的点可以不连通)

解决这类问题有一种通用的状压DP方法

设f[i][S]表示:当点i与这k个连通情况为S时,所花费的最小代价

那么有两种比较显然的转移

f[i][S]=f[i][T]+f[i][S^T]    (T是S的子集,先转移,保证在第二步转移时的dp值是最优的)

f[u][S]=f[v][S]+dis[u][v]   (v是u的临接点,后转移)

发现这样的时间复杂度为O((3^k)*n+(2^k)*SPFA(n,m))

(其实这种问题也比较板了)

 

现在,你已经学会了斯坦纳树,让我们来看一道例题

例题:

 

 

 

 

 

题解

(灵活运用)随机化+(灵活运用)斯坦纳树

随机选择k种颜色,然后把其他的颜色也随机地设为这k种颜色

现在的问题就变成了,求一个最小权值连通块,连通k种颜色(其实斯坦纳树并不一定非要状压关键点,它只是为状压DP提供了一种思路)

直接斯坦纳树就好了

这样随机化的准确率大概为k!/(k^k)

(就是对所有颜色做一个到k中颜色的映射,其中某k种颜色分配到的颜色互不相同的概率)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,K;
int a[20][20],ans,val[20][20],f[20][20][1<<7],id[3005],d[20][20],tmp[305];
bool vis[20][20];
struct node{int x,y,z;}q[1000005];
bool cmp(const node &x,const node &y){return x.z>y.z;}
int main()
{
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	srand(3993991);ans=INF;
	int i,j,k,l,x,y,_x,_y,w;
	scanf("%d%d%d",&n,&m,&K);
	for(i=1;i<=n;i++)for(j=1;j<=m;j++){
		scanf("%d",&a[i][j]);
		if(a[i][j]!=-1)a[i][j]++;
	}
	for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&val[i][j]);
	int T=500,N=n*m;
	for(i=1;i<=N;i++)id[i]=i;
	for(i=0;i<=n+1;i++)d[i][0]=d[i][m+1]=-1;
	for(j=0;j<=m+1;j++)d[0][j]=d[n+1][j]=-1;
	while(T--){
		random_shuffle(id+1,id+N+1);
		for(i=1;i<=K;i++)tmp[id[i]]=i;
		for(i=K+1;i<=N;i++)tmp[id[i]]=rand()%K+1;
		for(i=1;i<=n;i++)for(j=1;j<=m;j++)d[i][j]=tmp[a[i][j]]-1;
		int S=(1<<K);
		memset(f,0x3f,sizeof(f));
		for(i=1;i<=n;i++)for(j=1;j<=m;j++)
			if(d[i][j]>=0)f[i][j][1<<d[i][j]]=val[i][j];
		for(i=1;i<S;i++){
			int he=0,ta=0;
			for(l=(i-1)&i;l+l>i;l=(l-1)&i)
				for(j=1;j<=n;j++)for(k=1;k<=m;k++)
					f[j][k][i]=min(f[j][k][i],f[j][k][l]+f[j][k][i^l]-val[j][k]);
			memset(vis,0,sizeof(vis));
			for(j=1;j<=n;j++)for(k=1;k<=m;k++)if(f[j][k][i]<ans){
				q[++ta].x=j;q[ta].y=k;
				vis[j][k]=1;
			}
			while(he!=ta){
				x=q[++he].x;y=q[he].y;
				for(j=0;j<4;j++){
					_x=x+dx[j];_y=y+dy[j];w=f[x][y][i]+val[_x][_y];
					if(d[_x][_y]>=0&&w<ans&&w<f[_x][_y][i]){
						f[_x][_y][i]=w;
						if(!vis[_x][_y]){q[++ta].x=_x;q[ta].y=_y;vis[_x][_y]=1;}
					}
				}
				vis[x][y]=0;
			}
			for(x=1;x<=n;x++)for(y=1;y<=m;y++)for(j=0;j<4;j++){
				int _x=x+dx[j],_y=y+dy[j];w=f[x][y][i]+val[_x][_y];
				if(d[_x][_y]>=0&&w<ans)
					f[_x][_y][i|(1<<d[_x][_y])]=min(f[_x][_y][i|(1<<d[_x][_y])],w);
			}
		}
		for(i=1;i<=n;i++)for(j=1;j<=m;j++)ans=min(ans,f[i][j][S-1]);
	}
	if(ans==INF)printf("-1\n");
	else printf("%d",ans);
}