POJ-2226-Muddy Fields-二分图-最小点覆盖

http://poj.org/problem?id=2226


题意就是 给你一个r*c的矩阵。里面有一些*和.,要求用最少的操作去掉所有的*

每次的操作就是去掉一行连续的*,或者一列(长度任意)

。。但是这个题有个不一样的地方是, 每次操作不能碰到 点.  因此就是说不能粗暴的把整行去掉


之前的POJ3041就可以以点为边,对于点(x,y),只要对行x或着列y进行删除操作 都可以把点(x,y)抹去,也就是说,把行和列看作点,把点(x,y)看作边,把x行和y列连一条边,   最后只要找最少的x或y 覆盖所有的“边”即可


而这里也是同样,只不过 这里的行 操作不再是R行,而是先预处理一遍,看有多少连续的行,算出每个点所在 的第几个连续行里

例如:  *....*  的两个星号分别在第一个连续行操作,和第二个连续行操作。


因此 最后还是 把点(x,y)看成边,不过连接的点 分别是  该点所在的第i个连续行,和j个连续列,最后求一次最小点覆盖即可 


注意。连续行/列的数量可能超过 R/C


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;

char tm[51][51];


int min(int a,int b)
{return a<b?a:b;}
int row[51][51];
int col[51][51];
int g[51*51][51*51];
int uN,vN;
const int MAXN = 51*51;
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)
{
	for (int v=0;v<vN;v++)
		if (g[u][v]&&!used[v])	
		{
			used[v]=true;
			if (linker[v]==-1||dfs(linker[v]))
			{
				linker[v]=u;
				return true;
			}
		}
		return false;
}
int hungary()
{
	int res=0;
	memset(linker,-1,sizeof(linker));
	for (int u=0;u<uN;u++)
	{
		memset(used,false,sizeof(used));
		if (dfs(u)) res++;
	}
	return res;
}

int main()
{
	int i,j;
	int r,c;
	cin>>r>>c;
	for (i=1;i<=r;i++)
		scanf("%s",tm[i]+1);
	
	int cun=0;
	for (i=1;i<=r;i++)
	{
		for (j=1;j<=c;j++)
		{
			if (tm[i][j]=='.') continue;
			++cun;
			while(tm[i][j]=='*'&&j<=c) 
			{
				row[i][j]=cun;
				j++;
			}
		}
	}
	uN=cun;
	cun=0;
	for (i=1;i<=c;i++)
	{
		for (j=1;j<=r;j++)
		{
			if (tm[j][i]=='.') continue;
			++cun;
			while(tm[j][i]=='*'&&j<=r) 
			{
				col[j][i]=cun;
				j++;
			}
		}
	}
	vN=cun;
	
	
	for (i=1;i<=r;i++)
	{
		for (j=1;j<=c;j++)
		{
			if (tm[i][j]=='.') continue;
			int x=row[i][j];
			int y=col[i][j];
			g[x-1][y-1]=1;
		}
	}
	int ret=hungary();
	printf("%d\n",ret);
	
	
	
	
	return 0;
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值