FZOJ 2331 LYK loves graph

魔改最小斯坦纳树的题。。。

首先我们发现颜色太多了非常不好处理,一种套路是随机把这些颜色映射到 \(k\) 种颜色,随机很多次来保证正确性。最后的正确率大概是:

\[1-(1-\frac{k!}{k^k})^C \]

其中 \(C\) 为随机次数。

好了现在我们只有 \(k\le 7\) 种颜色了。考虑类似最小斯坦纳树的设状态:设 \(f[i][j][s]\) 表示包括第 \(i\) 行第 \(j\) 列的状态为 \(s\) 的连通块的最小代价。然后由于是点权所以方程式要稍微变动一下。

代码:

#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random> 

using namespace std;

mt19937 rnd(0); 
const int N=20,INF=1<<29;
int dx[10]={0,-1,0,1,0};
int dy[10]={0,0,1,0,-1};
int val[N][N],n,m,k,col[N][N],ans=INF,D[N*N],a[N][N],vis[N][N],f[N][N][2000];
priority_queue <pair<int,pair<int,int> > > q; 

void init()
{
	scanf("%d %d %d",&n,&m,&k);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			scanf("%d",&val[i][j]);
}

void Dijkstra(int s)
{
	memset(vis,0,sizeof(vis));
	while(!q.empty())
	{
		pair<int,int> p=q.top().second;q.pop();
		if(vis[p.first][p.second])
			continue;
		vis[p.first][p.second]=1;
		for (int i=1;i<=4;i++)
		{
			int tx=p.first+dx[i],ty=p.second+dy[i];
			if(tx<1||ty<1||tx>n||ty>m||col[tx][ty]==-1)
				continue;
			if(f[tx][ty][s]>f[p.first][p.second][s]+val[tx][ty])
			{
				f[tx][ty][s]=f[p.first][p.second][s]+val[tx][ty];
				q.push(make_pair(-f[tx][ty][s],make_pair(tx,ty)));
			}
		}
	}
}

void work()
{
	int T=300;
	while(T--)
	{
		for (int i=0;i<n*m;i++)
			D[i]=rnd()%k;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++)
				col[i][j]=(a[i][j]!=-1)?D[a[i][j]]:-1;
		int Max_N=1<<k;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++)
				for (int l=0;l<Max_N;l++)
						f[i][j][l]=INF;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++)
				if(col[i][j]!=-1)
					f[i][j][1<<col[i][j]]=val[i][j];
		for (int s=1;s<Max_N;s++)
		{
			for (int i=1;i<=n;i++)
				for (int j=1;j<=m;j++)
				{
					if(col[i][j]==-1) continue;
					for (int sub=(s-1)&s;sub;sub=(sub-1)&s)
						f[i][j][s]=min(f[i][j][sub]+f[i][j][s^sub]-val[i][j],f[i][j][s]);
					if(f[i][j][s]!=INF)
						q.push(make_pair(-f[i][j][s],make_pair(i,j)));
				}
			Dijkstra(s);
		}
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++)
				ans=min(ans,f[i][j][Max_N-1]);
	}
	printf("%d\n",ans==INF?-1:ans);
}

int main()
{
	init();
	work();
	return 0;
}
posted @ 2020-07-15 23:24  With_penguin  阅读(88)  评论(0编辑  收藏  举报