二分与图的映射(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  
  2 2 2
3 3 3  
    4  

按列:

1   4  
  3 4 5
2 3 4  
    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[55][55];
bool map2[2567][2567];
int R[55][55];
int C[55][55];
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[x])
		{
			vis[x]=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++)
			{
				if(map[i][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<=a;i++)
		{
			memset(vis,0,sizeof(vis));
			if(find(i)) ans++;
		}
		cout<<ans<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jack_jxnu/article/details/81168153
今日推荐