CF 108E Garden(DP,斯坦纳树)

题意:给你一个n*m的矩阵,和k个点,要求使这k个点相互连接,并且使连接的代价最小(每个矩阵上都有一个权值,如果权值为0表示k个点其中的一个,连接的代价等于将这些点连接起来的路径上的权值和。)

   简单的斯坦纳树,只要要要多开一个数组记录路径。如果不懂斯坦纳树,看http://endlesscount.blog.163.com/blog/static/821197872012525113427573/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define INF 1061109567
const int N=205;
const int M=300;
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};

int n,m,K;
int dp[N][M],pre[N][M];
int mat[N],vis[N],st[N];
bool in[N][M];
queue<int> que;

void spfa()
{
	while(que.size())
	{
		int top=que.front(); que.pop();
		int x=top/1000/m,y=(top/1000)%m,s=top%1000;
		in[x*m+y][s]=0;
		for(int i=0;i<4;i++)
		{
			int tx=x+dx[i],ty=y+dy[i];
			if(tx>=n||tx<0||ty>=m||ty<0) continue;
			int ts=s|st[tx*m+ty];
			if(dp[tx*m+ty][ts]>dp[x*m+y][s]+mat[tx*m+ty])
			{

				dp[tx*m+ty][ts]=dp[x*m+y][s]+mat[tx*m+ty];
				pre[tx*m+ty][ts]=top;
				if(in[tx*m+ty][ts]==0&&s==ts)
				{
				    in[tx*m+ty][ts]=1;
				    que.push( (tx*m+ty)*1000+ts );
				}
			}
		}
	}
}
void getans(int x,int y,int mask)
{
	//cout<<x<<" "<<y<<" "<<mask<<endl;
	vis[x*m+y]=1;
	int tmp=pre[x*m+y][mask];
	if(tmp==0) return;
	int tx=tmp/1000/m,ty=(tmp/1000)%m,s1=tmp%1000;
	getans(tx,ty,s1);
	if(tx==x&&ty==y)
        getans(tx,ty,((mask-s1)|st[x*m+y]));
}
int main()
{
	scanf("%d%d%d",&n,&m,&K);
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++) scanf("%d",&mat[i*m+j]);

	memset(dp,63,sizeof(dp));
	int a,b;
	for(int i=0;i<K;i++)
	{
		scanf("%d%d",&a,&b);
		a--;b--;
		st[a*m+b]=(1<<i);
		dp[a*m+b][(1<<i)]=mat[a*m+b];
	}

	int mask=(1<<K)-1;
	for(int s=1;s<=mask;s++)
	{
		for(int i=0;i<n*m;i++)
		{
		    if(st[i]&&!(st[i]&s)) continue;
			for(int p=(s-1)&s;p;p=(p-1)&s)
			{
				int s1=p|st[i],s2=(s-p)|st[i];
				int d=dp[i][s1]+dp[i][s2]-mat[i];
				if(d<dp[i][s])
				{
					pre[i][s]=i*1000+s1;
					dp[i][s]=d;
				}
			}
			if(dp[i][s]!=INF)
                in[i][s]=1,que.push(i*1000+s);
		}
		spfa();
	}
	printf("%d\n",dp[a*m+b][mask]);
	getans(a,b,mask);
	for(int i=0;i<n;i++,puts(""))
		for(int j=0;j<m;j++)
		{
			if(vis[i*m+j]) putchar('X');
			else putchar('.');
		}
	return 0;
}


你可能感兴趣的:(ini)