二分图概念及其相关算法:http://www.renfei.org/blog/bipartite-matching.html
本题难在如何建立二分图,详情看注释:
//MJRT
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
//
#include<iostream>
#include<algorithm>
#include<string>
#include <iterator>
#include<sstream>
#include<functional>
#include<numeric>
///
#include<vector>
#include<map>
#include <stack>
#include<queue>
#include<set>
#include <bitset>
#include <list>
using namespace std;
#define lch(x) ((x) << 1)
#define rch(x) ((x)<<1|1)
#define dad(x) ((x)>>1)
#define lowbit(x) ((x)&(-x))
static int INDEX = 0,BUGs = 0;
#define BUG() cout << "There is BUG No." << BUGs++ <<endl;
#define Whats(x) cout << "{ "<< #x << " }" << " is " << "*** "<< x << " ***" << " index:" << INDEX++ <<endl;
#define Show(x,s,l) cout << #x << ": "; for(int i = s ; i < s+l ; i++) cout << x[i] << " "; cout << "\n";
#define Clear(name) memset(name, 0, sizeof(name));
typedef long long int LL;
const int INF = ~0U>>1;
struct Point
{
int x,y;
Point(int x,int y):x(x),y(y){}
Point(){}
};
struct Line
{
Point s,e;
Line(Point s,Point e):s(s),e(e){}
Line(){}
bool operator ^ (const Line & b) const
{
return
max(s.x,e.x) >= min(b.s.x,b.e.x) &&
max(b.s.x,b.e.x) >= min(s.x,e.x) &&
max(s.y,e.y) >= min(b.s.y,b.e.y) &&
max(b.s.y,b.e.y) >= min(s.y,e.y) ;
}
};
const int N = 5 + 1250;
char maze[N][N];
bool mp[N][N];
int mark[N];
bool vis[N];
int m,n;
vector<Line> row,col;
bool dfs(int x);
int main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
#endif
int Case,tot=1;
scanf("%d",&Case);
while(Case--)
{
scanf("%d%d",&m,&n);
Clear(maze);
for(int i = 0 ;i < m ; i++) scanf("%s",&maze[i]);
//计算横着的联通块(需包含空地o)
row.clear();
row.push_back(Line(Point(0,0),Point(0,0)));
for(int i = 0 ;i < m ; i++)
{
bool ok = 0;
for(int j = 0 ; j < n ; )
{
while(maze[i][j] == '#' && j < n) j++;
int s = j;
while(maze[i][j] != '#' && j < n) j++;
int e = j-1;
for(int k = s ; k <= e ; k++) if(maze[i][k]=='o') {ok=1 ; break;}
if(s == e && maze[i][s] != 'o') ok = 0;
if(ok&&s<=e) row.push_back(Line(Point(i,s),Point(i,e)));
}
}
//计算竖着的联通块(需包含空地o)
col.clear();
col.push_back(Line(Point(0,0),Point(0,0)));
for(int i = 0 ; i < n ; i++)
{
bool ok = 0;
for(int j = 0 ; j < m ; )
{
while(maze[j][i] == '#' && j < m) j++;
int s = j;
while(maze[j][i] != '#' && j < m) j++;
int e = j-1;
for(int k = s ; k <= e ; k++) if(maze[k][i]=='o') {ok=1 ; break;}
if(s == e && maze[s][i] != 'o') ok = 0;
if(ok && s<=e ) col.push_back(Line(Point(s,i),Point(e,i)));
}
}
//建立二分图
//如果横着的联通块和竖着的联通块相交 且 交点是空地o ,我们认为他们是匹配的
Clear(mp);
for(int i = 1 ; i < row.size() ; i++ )
for(int j = 1 ; j < col.size() ; j++ )
if(row[i] ^ col[j] && maze[ row[i].s.x ][ col[j].s.y ] == 'o')
mp[i][j] = 1;
//为了方便重载n,m为二分图的长和宽
m = row.size()-1;
n = col.size()-1;
int cnt = 0;
Clear(mark);
for(int i = 1 ; i <= m ;i++)
{
Clear(vis);
if(dfs(i)) cnt++;
//else Whats(i);
}
int res = cnt;
printf("Case :%d\n",tot++);
printf("%d\n",res);
}
return 0;
}
bool dfs(int x)
{
for(int i = 1 ; i<= n ; i++)
{
if(!mp[x][i] || vis[i] ) continue;
vis[i] = 1;
if(!mark[i] || dfs(mark[i]))
{
mark[i] = x;
return 1;
}
}
return 0;
}