HDU1045:Fire Net(拆点+二分图)

题意是给一个最多4*4的网格图,其中有些地方障碍,现在在无障碍的格子放棋子,各个棋子不能同行同列,问最多可以放多少个棋上去。

如果是n*m的无障碍网格图,能放的最多的棋子个数是min(row,col)。
如果有障碍,因为这个图最多只有 4*4,完全可以枚举每个点放和不放,然后暴力搜索,check一下。
这里主要是讲二分图的思路:因为行列不能相同,以行,列建二分图。
一行有障碍的情况或一列有障碍的情况:如果一行或一列有障碍,(这里只说一下行的情况)一行实际上被拆成了几个互不影响的点,把一行每一段连通的部分进行染色,就有几个点,这些点是互不影响的。我们可以对整张图这样处理,对每一行都这样染色,每一列也这样染色,建出新的二分图。
现在,无障碍的情况其实就是特殊情况,每一行每一列全是连通的,都只代表一个点。
建图的时候,我们可以开到2个数组标记每一个点所属的新的行点,和新的列点。然后根据这个建二分图(行指向列),跑一下最大匹配即可。

这道题有意义的地方就在这拆点处理然后跑二分图。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e2+10;
char mat[maxn][maxn];
int belong[maxn][maxn];
int to[maxn][maxn],link[maxn][maxn],used[maxn],nxt[maxn];
int n;
bool find(int u,int col){
 	for(int i = 1; i <= col ; i++){
  		if(link[u][i] && !used[i]){
   			used[i] = 1;
   			if(!nxt[i] || find(nxt[i],col)){
   	 			nxt[i] = u;
    				return true;
   			}
  		}
 	}
 	return false;
}
int match(int row,int col){
 	int res = 0;
 	for(int i = 1; i <= row; i++){
  		memset(used,0,sizeof(used));
  		if(find(i,col)) res++;
 	}
 	return res;
}
int main(){
 	while(scanf("%d",&n)&&n){
  		for(int i = 1; i <= n; i++)
  	 		scanf("%s",mat[i]+1);
  		memset(link,0,sizeof(link));
  		memset(belong,0,sizeof(belong));
  		memset(to,0,sizeof(to));
  		memset(nxt,0,sizeof(nxt));
  		int col,row,cnt;
  		int len = 0;
  		for(int i = 1; i <= n; i++){
   			len ++;
   			cnt = 0;
   			for(int j = 1; j <= n; j++){
    				if(mat[i][j] == '.'){
     				if(cnt && mat[i][j-1] == 'X') len++;
     				belong[i][j] = len;
     				cnt ++ ;
    				}
   			}
  		}	  
  		row = len;
  		len = 0;
  		for(int i = 1; i <= n; i++){
   			len ++;
   			cnt = 0;
   			for(int j = 1; j <= n; j++){
    				if(mat[j][i] == '.'){
     				if(cnt && mat[j-1][i] == 'X') len++;
     				to[j][i] = len;
     				cnt ++ ;
    				}
   			}   
  		}
  		col = len;
  		//cout << col << " " << row <<endl;
  		for(int i = 1; i <= n; i++)
   			for(int j = 1; j <= n; j++)
    				link[belong[i][j]][to[i][j]] = 1;
  		cout << match(row,col) << endl;
  		//cout << min(row,col) << endl;
 	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值