E (5). 卡片覆盖(二分图,匈牙利算法)

题目描述

有许多大小为1×2的小卡片,有一个m×n棋盘,上面有k个洞。可以将卡片覆盖在棋盘上(卡片必须整个覆盖在棋盘上),正好覆盖一个1×2的空间,但是不能覆盖有洞的棋盘格子。任务是能否用一些卡片(不限个数),将棋盘的无洞格子全都覆盖?图显示了覆盖是否合法。 

输入描述

有多组数据,每组数据第一行有三个整数m,n,k(0<m,n≤32 0≤k<m×n),随后k行,每行两个整数,表示洞的坐标。

输出描述

如果可以输出“YES”,否则输出“NO”。

 

分析

        因为一张小卡片需要相邻两个格子组成,我们将整个棋盘将相邻的格子染上不同颜色,就像是国际象棋棋盘一样,如下图。在这样染色后,我们要保证每个白格和棕格一一匹配。

若删去格子数为奇数时,至少有一个格子没有匹配对象。

若删去格子数为偶数时,则若最大匹配数==剩余格子数/2时,覆盖合法,否则不合法

代码

#include<bits/stdc++.h>
using namespace std;
 
int vis[1001],d[1001][1001],st[50][50];
int p[1001];
int dir[4][2]= {0,1,1,0,-1,0,0,-1};
int N,M,n,m,k;
int u,v;
 
bool match(int e) {
	for(int i=1; i<=N; i++) {
		if(vis[i]||!d[e][i])
			continue;
		vis[i]=true;
		if(p[i]==0||match(p[i])) {
			p[i]=e;
			return true;
		}
	}
	return false;
}
int is_cnt() {
	int cnt=0;
	for(int i=1; i<=M; i++) {
		memset(vis, false, sizeof vis);
		if(match(i))
			cnt++;
	}
	return cnt;
}
 
 
int main() {
	cin>>n>>m>>k;
	for(int i=1; i<=k; i++)
		cin>>u>>v,st[u][v]=-1;//删去格子的标记
	if(k&1) {//奇数一定不合法
		cout<<"NO";
		return 0;
	}
	bool f=0;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=m; j++) {
			if(st[i][j]==-1)
				continue;
            
            //对格子的染色分组
			if(!f)
				st[i][j]=++N;
			else
				st[i][j]=++M;
			f=!f;
		}
	for(int i=1; i<=n; i++)
		for(int j=1; j<=m; j++)
			for(int k=0; k<4; k++) {
				int dx=i+dir[k][0],dy=j+dir[k][1];
				if(st[dx][dy]==-1)
					continue;
				d[st[i][j]][st[dx][dy]]=true;//在相邻格子间连一条边
			}
	if(n*m-k==is_cnt()*2)
		cout<<"YES";
	else
		cout<<"NO";
	return 0;
}
/*
解决方案: 873718
问题: 竞赛3162:E (5)
用户: 20222203362
判题类型: acm
提交时间: 2023-05-26 11:34:22
时间: 12MS
空间: 0.63MB
语言: C++20
代码长度: 1083B
判题时间: 2023-05-26 11:34:24
 
panjyash原创 禁止转载!!!
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

panjyash

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值