题意:给你一个二维网格图,让你在网格上尽可能多的放blockhouse,每行或者每列最多放一个,除非中间有东西隔开。
分析:这题其实就是上次上海邀请赛的D题啊,只不过网格大小改成50x50而已。当然这里你爆搜也是可以的。我们来看看二分匹配的思路。首先我们拿掉障碍物,这种情况下,每行每列只能放一个物品,我们直接以行和列为X,Y集合直接建图即可。但是这题图中加了障碍物,每行或者每列可能放多个物品。我们发现每行或者每列中,连续的可放物品的格子,在相应的行和列上是等价的,可以缩为一点(标号相同),这样我们也形成了二分匹配的两个集合。当网格为点时,add(h[i][j],l[i][j])建图即可。再回过头来看一下,当不放障碍物时,我们也这样进行标号,可以发现最后的行列标号,和原来是一样的,你有没有发现这类题型建图的相通性?
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <string>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
typedef long long ll; //记得必要的时候改成无符号
const int maxn=505; //点数
const int maxm=1000005; //边数
struct EdgeNode
{
int to;
int next;
}edge[maxm];
int head[maxn],cnt;
void add(int x,int y)
{
edge[cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt++;
}
int n,m,v[maxn],pre[maxn];
int h[maxn][maxn],l[maxn][maxn];
char g[maxn][maxn];
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
bool dfs(int x)
{
int y;
for(int i=head[x];i!=-1;i=edge[i].next)
{
y=edge[i].to;
if(!v[y])
{
v[y]=1;
if(!pre[y]||dfs(pre[y]))
{
pre[y]=x;
return true;
}
}
}
return false;
}
int erfenpipei()
{
int sum=0;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)
{
memset(v,0,sizeof(v));
if(dfs(i))
sum++;
}
return sum;
}
int main()
{
int i,j,hs,ls;
while(~scanf("%d",&n)&&n)
{
init();
hs=ls=0;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>g[i][j];
for(i=1;i<=n;i++){
hs++;
for(j=1;j<=n;j++){
if(g[i][j]=='X')hs++;
else h[i][j]=hs;
}
}
for(j=1;j<=n;j++){
ls++;
for(i=1;i<=n;i++){
if(g[i][j]=='X')ls++;
else l[i][j]=ls;
}
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(g[i][j]=='.')add(h[i][j],l[i][j]);
n=hs;
printf("%d\n",erfenpipei());
}
return 0;
}