二分与图的映射(POJ3041、2226)

题目链接:3041 Asteroids

                   2226 Muddy Fields

先说行星这题,题意说可以一次操作消去一行或一列的行星,问全部消去所需要的最小操作次数。 

给出例图:

#

 

#

 

#

 

 

#

  

按行消:

1 1
 2 
 3 

按列消:

1 3
 2 
 2 

由此我们构造出一个按点消去的map2;

对于第一点:1-1;第三点:1-3;第五点:2-2;第八点:3-2。接下来用匈牙利算法找出map2的最大匹配数。

对于我们得出的这四条线,均是按点连接行和列的关系,如第一点,可以通过消去第一行或第一列消去第一点。

等等,我们明明要找最小消去步数,怎么突然变成了找最大匹配数?

原因很简单,因为我们首先要确定全部消去,故而找的是行和列的最大匹配数(才能保证全部消去),而在查找最大匹配数时,我们总是尽量让所有行匹配上列(匈牙利算法的要求),所以只要全部匹配成功,那么自然求出的就是最小步数了。

再看田野这一题,题目是按行或列消,但是遇到" . "就不能继续消了。

对于例子:

# # 
 ###
### 
  # 

按行:

1 1 
 222
333 
  4 

按列:

1 4 
 345
234 
  4 

构建map2:

1-1、1-4、2-3、2-4、2-5、3-2、3-3、3-4、4-4。

不多话,附上代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
bool map[1055][1055];
bool map2[2567][2567];
int R[1055][1055];
int C[1055][1055];
bool vis[2567];
int linker[2567];
int Rn,Ln;
bool find(int x)
{
	for(int i=1;i<=Ln;i++)
	{
		if(map2[x][i]&&!vis[i])
		{
			vis[i]=1;
			if(!linker[i]||find(linker[i]))
			{
				linker[i]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	int r,l;
	char c;
	while(cin>>r>>l)
	{
		for(int i=1;i<=r;i++)
		{
			for(int j=1;j<=l;j++) 
			{
				cin>>c;
				if(c=='*') map[i][j]=1;
				else map[i][j]=0;
			}
			getchar();
		}

		int a=0,b=0;
		for(int i=1;i<=r;i++)
		{
			for(int j=1;j<=l;j++)
			{
				if(map[i][j]==1)a++;
				while(map[i][j]==1) R[i][j++]=a;
				
			}
		}
		
		for(int i=1;i<=l;i++)
		{
			for(int j=1;j<=r;j++)
			{
				if(map[j][i]==1) b++;
				while(map[j][i]==1) C[j++][i]=b;
				
			}
		}
		for(int i=1;i<=r;i++)
		{
			for(int j=1;j<=l;j++)
			{
				map2[R[i][j]][C[i][j]]=1;
			}
		}
		Rn=a;
		Ln=b;
		int ans=0;
		memset(linker,0,sizeof(linker));
		for(int i=1;i<=Rn;i++)
		{
			memset(vis,0,sizeof(vis));
			ans+=find(i);
		}
		cout<<ans<<endl;
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值