hdu 1045 Fire Net(二分图)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1045

题目大意为给定一个最大为4*4的棋盘,棋盘可以放置堡垒,处在同一行或者同一列的堡垒可以相互攻击,在棋盘上也有城墙,可以隔离同一行同一列堡垒的相互攻击,“X”表示有城墙,“ . ”表示空地,问该棋盘最多可以放置多少个堡垒?

此题为二分图匹配问题,但是起初我不知道怎么建图,看题解发现一个规律,同一行同一列的连续空地不能放置两个堡垒,那么同一行同一列的连续空地的交点放置一个堡垒后,该行该列将不能再放置堡垒,这样很容易联想到二分图的性质,那么把连续“行”空地设为l集合,连续的“列”空地设为r集合,也就是说把连续行空地和连续列空地缩点,如果两者在原图中有交点,则可以建边,这样便可以轻易建图了,建图之后跑一下匈牙利算法即可得出答案,其二分图最大匹配数则为该棋盘最多可以放置的堡垒。

AC代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1001;
int visit[maxn];//访问数组 
bool g[maxn][maxn];//建立二分图 
int match[maxn];//匹配数组 
int l,r;//l和r两集合 

bool dfs(int u){
	// i为建图后r集合的节点 ,u为l集合的节点 
	for(int i = 1;i<=r;i++){//依次遍历集合r 
		if(g[u][i] && !visit[i]){//如何u和i之间有边,而且i没有被访问过 
			visit[i] = true;//标记 
			if(match[i] == -1 || dfs(match[i])){//如果该i没有匹配过则直接匹配u,
			 //如果匹配过那么尝试dfs寻找增广路 
				match[i] = u;
				return true;
			}
		}
	}
	return false;
}

int hungary(){
	int ans = 0;
	memset(match,-1,sizeof(match));//初始化r匹配的 l
	for(int i = 1;i<=l;i++){
		memset(visit,false,sizeof(visit));//初始化l的visit数组,表示都没有访问过 
		if(dfs(i)){//跑dfs遍历 
			ans++;//找到一个匹配 
		}
	}
	return ans;
}
int main(){
	int n;
	vector<int> res;
	while(~scanf("%d",&n))
	{
		memset(g,false,sizeof(g));//初始化图 
		int markl[5][5];
		vector<string> s;
		
		if(n==0){
			break;
		}
		
		for(int i = 0;i<n;i++){
			string t;
			cin>>t;
			s.push_back(t); //存string数据 
		}
		
		int step = 0;//step标记点的序号 
		for(int i = 0;i<n;i++){
			for(int j = 0;j<n;j++){
				//如下是缩点操作 
				if(s[i][j] == '.'){
					if(j == 0){
						step++;
						markl[i][j] = step;
						continue;
					}
					if(s[i][j-1] == 'X'){
						step++;
					}
					markl[i][j] = step;
				}
			}
		}
		l = step;//记录集合l的数量 

		int markr[5][5];
		step = 0;
		for(int j = 0;j<n;j++){
			for(int i = 0;i<n;i++){
				if(s[i][j] == '.'){
					if(i == 0){
						step++;
						markr[i][j] = step;
						continue;
					}
					if(s[i-1][j] == 'X'){
						step++;
					}
					markr[i][j] = step;
				}
			}
		}
		r = step;
		
		for(int i = 0;i<n;i++){
			for(int j = 0;j<n;j++){
				if(s[i][j] == '.')
				    g[markl[i][j]][markr[i][j]] = true;//如果缩点后两者有交点,则在邻接矩阵上建边 
			}
		}
		cout<<hungary()<<endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AaronChang/p/12129650.html