算法竞赛——进阶指南——acwing377. 泥泞的区域 2要素,转化二分图求最小点覆盖

2要素即:每条边有2个端点,两者至少选择一个。求最小选择的端点数。

对于这一题:

我们发现:每个泥泞格子要么被列木板覆盖,要么被横向模板覆盖,且至少选择一种类型的木板。

显然这符合2要素,直接转化为二分图模型来做:

 

我们对每一个泥泞的格子视为一条边,连接某一列木板,和某一行木板。

注意,第i行可能有多个木板,我们要赋予他们不同的id。

比如:

.**...**.  这一行

前面的2个泥泞格子与后面的泥泞格子所需要的行木板是不相同的。

预处理出id,直接二分图最大匹配即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e4+7;

int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}

char s[100][100];
int n,m;
int gt(int x,int y)
{
	return (x-1)*n+y;
}
int r[100][100];
int c[100][100];
int vs[M],match[M];
bool dfs(int x)
{
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to;
		if(vs[y])continue;
		vs[y]=1;
		if(!match[y]||dfs(match[y]))
		{
			match[y]=x;
			return true;
		}
	}
	return false;
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	cin>>n>>m;
  	for(int i=1;i<=n;i++)cin>>(s[i]+1);
  	int sz=0,ln=0;
  	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
	  	{
	  		if(s[i][j]=='*')
	  		{
	  			if(s[i][j-1]!='*')r[i][j]=++sz;
	  			else r[i][j]=sz;
			}
		}
	ln=sz;//左部图的数量,即横向板子的数量 
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
	  	{
	  		if(s[j][i]=='*')
	  		{
	  			if(s[j-1][i]!='*')c[j][i]=++sz;
	  			else c[j][i]=sz;
			}
		}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		if(s[i][j]=='*')
		{
			add(r[i][j],c[i][j]);
			add(c[i][j],r[i][j]);
			
		}
	}
	int ans=0;
//	cout<<sz<<" "<<ln<<endl;
	//横向板和纵向板为点,每个泥泞为边,求最小点覆盖。
	for(int i=1;i<=ln;i++)
	{
		memset(vs,0,sizeof(vs));
		if(dfs(i))ans++;
	}
	cout<<ans<<endl;
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值