棋盤類二分圖匹配 - Chessboard(POJ 2446)

傳送門


Analysis

可以根據i和j性質可以看出,i+j爲奇數的上下相鄰的i’和j’一定是偶數,那麼一個1*2的紙片的i+j一定是一個奇數一個偶數。所以建立一個二分圖兩個集合,將i+j爲奇數的點與上下左右相鄰的點連在一起,當然點不是洞。最後就用匈牙利算法求最大匹配數


Code
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define in read()
#define re register
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
}
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int n,m,K;
int pos[35][35],vis[2000],match[2000];
inline int idx(int x,int y){return (x-1)*m+y;}
int nxt[10000],head[2000],to[10000],ecnt=0;
inline void add(int x,int y){
	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;
}
int ans=0,dfn=0;
inline int find(int u){
	for(re int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(vis[v]==dfn) continue;
		vis[v]=dfn;
		if(match[v]==-1||find(match[v])){
			match[v]=u;
			return 1;
		}
	}
	return 0;
}
void km(){
	memset(match,-1,sizeof(match));
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=m;++j){
			if(pos[i][j]||(i+j)%2==0) continue;
			++dfn;
			ans+=find(idx(i,j));
		}
}
int main()
{
	n=in;m=in;K=in;
	for(re int i=1;i<=K;++i) {
		int y=in,x=in;
		pos[x][y]=1;
	}	
	for(re int i=1;i<=n;++i){
		for(re int j=1;j<=m;++j){
			if(pos[i][j]||(i+j)%2==0) continue;
			for(re int k=0;k<4;++k){
				int x=i+dx[k];
				int y=j+dy[k];
				if(x>=1&&y>=1&&x<=n&&y<=m){
					if(pos[x][y]) continue;
					add(idx(i,j),idx(x,y));
				}
			}
		}
	}
	km();
	if(ans*2+K==n*m) printf("YES");
	else printf("NO");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章