【BZOJ4883】棋盘上的守卫(最小生成树)

【BZOJ4883】棋盘上的守卫(最小生成树)

题面

BZOJ

题解

首先\(n\)\(m\)列的棋盘显然把行列拆开考虑,即构成了一个\(n+m\)个点的图。我们把格子看成边,那么点\((x,y)\),看成\(x\)\(y\)的一条边,方向自己随便定。那么我们的任务就是选择一些边,使得所有点的入度至少为\(1\),既然要最小则显然为恰好为\(1\)。那么我们现在有\(n*m\)条边,\(n+m\)个点,要构建一个\(n+m\)个点的图,显然这个玩意是一个基环森林,类似克鲁斯卡尔一样的维护一下即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 100100
#define ll long long
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
ll ans;
int n,m,tot,f[MAX];
bool cir[MAX];
struct edge{int u,v,w;}E[MAX];
bool operator<(edge a,edge b){return a.w<b.w;}
int id(int i,int j){return (i-1)*m+j;}
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n+m;++i)f[i]=i;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			E[++tot]=(edge){i,j+n,read()};
	sort(&E[1],&E[tot+1]);
	for(int i=1;i<=tot;++i)
	{
		int u=getf(E[i].u),v=getf(E[i].v);
		if(u==v){if(!cir[u])cir[u]=true,ans+=E[i].w;}
		else if(!cir[u]||!cir[v])
			f[u]=v,ans+=E[i].w,cir[v]|=cir[u];
	}
	printf("%lld\n",ans);return 0;
}
posted @ 2018-09-25 11:32  小蒟蒻yyb  阅读(442)  评论(0编辑  收藏  举报