题意是给一个最多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;
}
}