题意:给你一个m*n(1<=m,n<=50)的图,其中 ‘#’代表冰山, ‘*’代表海洋, ‘o’代表浮冰。。然后让你尽可能放置最多的船,但是要满足一下规矩:

  1. 船不能放在冰山上;
  2. 船不能放到浮冰上
  3. 两艘船之间除非中间有冰山,否则不能在同一列或同一行。

分析:红果果的二分最大匹配。。。。图也很容易构造。。。把每一行被冰山分隔开来的海洋格子连通块(至少一个格子)作为X点,同样的每一列被冰山分隔开来的海洋格子连通块作为Y点,X点与Y点有边相连当且仅当这两个连通块共用一个海洋格子。然后跑二分最大匹配。。。2500个点,妥妥的。

以好像是官方题解,但过不了,把next数组改成nextt能过c++的,过不不了g++。

#include<stdlib.h>
#include<cmath>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdio>
using namespace std;
#define N 205
#define M 50005
int ev[M],nextt[M];
int head[M];
int cnt;
bool vis[M];
int pre[M];
char s[N][N];
int x[N][N],y[N][N];
int pn[2];
void addedge(int u,int v){
ev[cnt]=v;
nextt[cnt]=head[u];
head[u]=cnt++;
return;
}
int find(int x){
for(int i=head[x];~i;i=nextt[i]){
int v=ev[i];
if(!vis[v]){
vis[v]=1;
if(pre[v]==-1||find(pre[v]))
{
pre[v]=x;
return 1;
}
}
}
return 0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
memset(head,-1,sizeof(head));
cnt=0;
memset(pre,-1,sizeof(pre));
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%s",s[i]);
int num=0;
for(int i=0;i<n;i++)
{
bool flag=0;
for(int j=0;j<m;j++){
if(s[i][j]=='*'){
if(flag==0)
num++;
x[i][j]=num;
flag=1;
}
else if(s[i][j]=='#')
flag=0;
}
}
pn[0]=num;
num=0;
for(int i=0;i<m;i++)
{
bool flag=0;
for(int j=0;j<n;j++){
if(s[j][i]=='*'){
if(flag==0) num++;
y[j][i]=num;
flag=1;
}
else if(s[j][i]=='#')
flag=0;
}
}
pn[1]=num;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
int u = x[i][j],v=y[i][j];
if(u&&v) addedge(u,v);
}
int ans=0;
for(int i=1;i<=pn[0];++i)
{
memset(vis,0,sizeof(vis));
ans+=find(i);
}
printf("%d\n",ans);
}
return 0;
}