分析:
在棋盘游戏的基础上(两点不能在同行同列),加了一个墙的概念,如果两个点之间有墙是可以在同一行/列的。所以就不能单纯地用行和列来建二分图进行匹配了,需要进行缩点重新建图。题解:
读取输入的时候可以用getchar(),需要注意的是,每读取完一行需要用一个getchar()来拿走那个换行符。读取完毕后,进行缩点:先把相同的行区域缩成一个点,便利每一个点,如果该点是空的并且它的行区域没有标号,就从这点开始在同一行往后找,把所有连续的空点标上相同的序号,直到碰到墙或者结束,然后行区域序号+1.列区域的标号同样。缩点操作后开始建图,如果某点为空,那么它所属的行区域和列区域就可以连上一条边,以此遍历所有点,连上边。然后用匈牙利算法即可算出最大匹配。AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#define N 6
using namespace std;
char Map[N][N];
int flag[N];
int row[N][N], col[N][N], r[N], c[N];
int cnt_col,cnt_row;
bool path[N][N];
int DFS(int x)
{
for(int i=0;i<cnt_col;i++)
{
if(!path[x][i] || flag[i])//如果行区域x与列区域i之间没有边 或者再搜x的情况里已经被搜索过了,就跳过。
continue;
flag[i] = 1;
if( c[i] == -1|| DFS(c[i]))//如果这个列区域i没有匹配或者它还能匹配到其它的行区域就更新匹配。
{
c[i] = x;
r[x] = i;
return 1;
}
}
return 0;
}
void Hungarian()
{
int ans = 0;
memset(r, -1, sizeof(r));
memset(c, -1, sizeof(c));
for(int i=0;i<cnt_row;i++)
{
if(r[i] == -1)//如果该行区域未匹配就用DFS搜
{
memset(flag, 0, sizeof(flag));
ans+=DFS(i);
}
}
cout << ans << endl;
}
int main()
{
int n;
while(scanf("%d%*c", &n), n)
{
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
Map[i][j] = getchar();
getchar();
}
memset(row, -1, sizeof(row));
memset(col, -1, sizeof(col));
cnt_row = cnt_col = 0;
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
if(Map[i][j] == '.' && row[i][j] == -1)
{ //横向缩点
for(int k = j; Map[i][k] == '.' && k < n; k++)
row[i][k] = cnt_row; //给相同的行区域标记同一个数字
cnt_row++;
}
if(Map[j][i] == '.' && col[j][i] == -1)
{ //纵向缩点
for(int k = j; Map[k][i] == '.' && k < n; k++)
col[k][i] = cnt_col; //给相同的列区域标记同一个数字
cnt_col++;
}
}
}
memset(path, false, sizeof(path));
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
if(Map[i][j] == '.') //连边,'.'的地方即为缩点后的 行和列的交点
path[ row[i][j] ][ col[i][j] ] = true;
Hungarian();
}
return 0;
}