loj6033.「雅礼集训 2017 Day2」棋盘游戏

题意

见到棋盘首先想黑白染色,之后对这个二分图跑一个最大匹配。

假如一个棋子放在了一个非匹配点上,那么先手必败,因为后手每次都有匹配边可以走。

于是我们要求有多少个点可能不在一个最大匹配中:
考虑一个非匹配点(x),假如它连向了一个匹配点(y),那么它就能替换掉当前当前和这个匹配点匹配的点(z),而这时(z)也可以进行同样的操作。

于是我们从每个非匹配(x)出发进行(dfs),途经的和(x)同一集合的点都满足要求。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=150;
int n,m,cnt_edge,tim,ans;
int head[maxn*maxn],match[maxn*maxn],vis[maxn*maxn];
char a[maxn][maxn];
bool check[maxn*maxn];
inline int read()
{
	char c=getchar();int res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
struct edge{int to,nxt;}e[maxn*maxn*8]; 
inline void add_edge(int u,int v)
{
    e[++cnt_edge].nxt=head[u];
    head[u]=cnt_edge;
    e[cnt_edge].to=v;
}
inline int id(int i,int j){return (i-1)*m+j;}
bool dfs(int x)
{
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(vis[y]==tim)continue;
		vis[y]=tim;
		if(!match[y]||dfs(match[y])){match[y]=x;match[x]=y;return 1;}
	}
	return 0;
}
void dfs_ans(int x)
{
	check[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(!check[match[y]])dfs_ans(match[y]);
	}
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(a[i][j]=='#')continue;
			if(i<n&&a[i+1][j]=='.')add_edge(id(i,j),id(i+1,j)),add_edge(id(i+1,j),id(i,j));
			if(j<m&&a[i][j+1]=='.')add_edge(id(i,j),id(i,j+1)),add_edge(id(i,j+1),id(i,j));
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(((i+j)&1)&&a[i][j]=='.')
				tim++,dfs(id(i,j));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]=='.'&&!match[id(i,j)]&&!check[id(i,j)])
				dfs_ans(id(i,j));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(check[id(i,j)])ans++;
	printf("%d
",ans);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(check[id(i,j)])printf("%d %d
",i,j);
	return 0;
}
原文地址:https://www.cnblogs.com/nofind/p/12149757.html