题目:http://poj.org/problem?id=3020
AC代码(C++):
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <vector>
#include <queue>
#include <math.h>
#include <string>
#include <string.h>
#include <bitset>
#define INF 0xfffffff
#define MAXN 45
using namespace std;
int n,m,k;
int gripint[MAXN][MAXN];
int map[MAXN*MAXN][MAXN*MAXN];
int visit[MAXN*MAXN],flag[MAXN*MAXN];
bool dfs(int a)
{
for(int i=1; i<=k; i++)
{
if(map[a][i]&&!visit[i])
{
visit[i]=1;
if(flag[i]==0||dfs(flag[i]))
{
flag[i]=a;
return true;
}
}
}
return false;
}
int main(){
int t;
cin>>t;
for(int tt = 0; tt < t; tt++){
cin>>n>>m;
k=0;
memset(map,0,sizeof(map));
memset(gripint,0,sizeof(gripint));
char tmp;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin>>tmp;
if(tmp=='*'){
k++;
gripint[i][j] = k;
}
else gripint[i][j] = 0;
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(gripint[i][j]!=0){
if(i-1>=0&&gripint[i-1][j]!=0){
map[gripint[i][j]][gripint[i-1][j]]=1;
}
if(i+1<n&&gripint[i+1][j]!=0){
map[gripint[i][j]][gripint[i+1][j]]=1;
}
if(j-1>=0&&gripint[i][j-1]!=0){
map[gripint[i][j]][gripint[i][j-1]]=1;
}
if(j+1<m&&gripint[i][j+1]!=0){
map[gripint[i][j]][gripint[i][j+1]]=1;
}
}
}
}
memset(flag,0,sizeof(flag));
int result=0;
for(int i=1; i<=k; i++){
memset(visit,0,sizeof(visit));
if(dfs(i))result++;
}
cout<<k-result/2<<endl;
}
}
总结: 第一眼看感觉无从下手, 其实是一道二分图匹配的题目, 关键是要怎样抽象成二分图. 把*看做是顶点, 构成一个顶点集合A, 因为题目是顶点间互相匹配所以再构造一个和A相同的集合B. 两顶点在grip上相邻则在AB间有条边, 例如顶点1和顶点2相邻, 则A中的1和B中的2有一条边, A中的2和B中的1也有一条边. 这样就构成了一个二分图, 之后用匈牙利算法算出匹配数. 因为正如刚才举例说的, 顶点1和顶点2相邻则生成2条边, 其实最大匹配覆盖了双倍的边, 则匹配数应该除二. 因为椭圆可以重叠, 即对某个顶点可能有多次匹配, 则应该用顶点数减匹配数. 所以最后答案=顶点数-匹配数/2.