题干:

Dear contestant, now you are an excellent navy commander, who is responsible of a tough mission currently. 

Your fleet unfortunately encountered an enemy fleet near the South Pole where the geographical conditions are negative for both sides. The floating ice and iceberg blocks battleships move which leads to this unexpected engagement highly dangerous, unpredictable and incontrollable. 

But, fortunately, as an experienced navy commander, you are able to take opportunity to embattle the ships to maximize the utility of cannons on the battleships before the engagement. 

The target is, arrange as many battleships as you can in the map. However, there are three rules so that you cannot do that arbitrary: 

A battleship cannot lay on floating ice 
A battleship cannot be placed on an iceberg 

Two battleships cannot be arranged in the same row or column, unless one or more icebergs are in the middle of them. 

Input

There is only one integer T (0<T<12) at the beginning line, which means following T test cases. 

For each test case, two integers m and n (1 <= m, n <= 50) are at the first line, represents the number of rows and columns of the battlefield map respectively. Following m lines contains n characters iteratively, each character belongs to one of ‘#’, ‘*’, ‘o’, that symbolize iceberg, ordinary sea and floating ice.

Output

For each case, output just one line, contains a single integer which represents the maximal possible number of battleships can be arranged.

Sample Input

2
4 4
*ooo
o###
**#*
ooo*
4 4
#***
*#**
**#*
ooo#

Sample Output

3
5

题目大意:

给出一个n行m列的图,*代表海域,o代表冰水,#代表冰山,要想在海域中放置船,保证船与船之间不能相互看到,之间只要有山就不能看到,问最多能放多少船。

(也就是冰水和冰山都不能放船,但是冰山就能隔开两个船,冰水就不行)
将一片最多只能放一个船的连续网格叫做‘块’。
以样例一为例
首先只考虑行,将每个块标号:将答案存入x [ ] [ ]
 1000
 0000
 2203
 0004
再此只考虑列,将每个块标号:将答案存入y [ ][ ]
 1000
 0000
 2304
 0004

解题报告:

   建图,注意那个line数组那些,不要开数组开小了,因为totx和toty都可能比较大。

其实是这个题的进化版本:​​【BZOJ - 1059】矩阵游戏(二分图匹配,建图,最小边覆盖)​​。

不同点在于,他那个题不需要缩点,就相当于你这个题中每一行和每一列就是自己一个连通块,中间没有东西把他们分割开来,也就是说如果没有 ' # ' ,那么就退化成bzoj这个题了。

这题也是这样,二分图中的边代表原图中的每一个点(这里的点指的是连通块的某一个交点),当选择了某一条边,也就说明连接的左右两个点已经被占用了(代表对应的行连通块和列连通块被使用了)

思考方式和这个题也很像【HDU - 1281 】

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
char maze[555][555];
int visx[555][555],visy[555][555];
int n,m;
int totx,toty;
bool line[2500][2500];
int nxt[2505];
bool used[2555];
bool find(int x) {
for(int i = 1; i<=toty; i++) {
if(line[x][i] && used[i]==0) {
used[i]=1;
if(nxt[i] == -1 || find(nxt[i])) {
nxt[i] = x;
return 1;
}
}
}
return 0;
}
int match() {
int sum = 0;
memset(nxt,-1,sizeof nxt);
for(int i = 1; i<=totx; i++) {
memset(used,0,sizeof used);
if(find(i)) sum++;
}
return sum;
}
int main()
{
int t;
cin>>t;
while(t--) {
scanf("%d%d",&n,&m);
memset(visx,0,sizeof visx);
memset(visy,0,sizeof visy);
memset(line,0,sizeof line);
totx=toty=0;
for(int i = 1; i<=n; i++) scanf("%s",maze[i]+1);
for(int i = 1; i<=n; i++) {
for(int j = 1; j<=m; j++) {
if(maze[i][j] == '*' || maze[i][j] == 'o') {
if(maze[i][j-1]=='#' || j == 1) visx[i][j] = ++totx;
else visx[i][j] = visx[i][j-1];
if(maze[i-1][j]=='#' || i == 1) visy[i][j] = ++toty;
else visy[i][j] = visy[i-1][j];
}
}
}
for(int i = 1; i<=n; i++) {
for(int j = 1; j<=m; j++) {
if(maze[i][j] == '*') {
line[visx[i][j]][visy[i][j]] = 1;
}
}
}
printf("%d\n",match());
}
return 0 ;
}