二分图最大匹配
因为两个差不多,这里只写HDU的。交BZOJ的话把t给删了就行。
题目大意:给你n*m的一个图,其中‘*’表示正常海面,‘#’表示冰山,‘o’表示浮冰。你要放尽可能多的船。船不能放在浮冰或冰山上。同一行同一列只能放一条船,除非中间有冰山隔着。输出最多能放的船的数量。
思路:先只考虑行的情况,把每只战船影响的范围称为一个”块“。给每一个块编号,把结果保存在一个二维数组中。x[i][j]存坐标为i,j的编号。列的话同样做,存在另一个数组中。
我们可以把行看成A集合,把列看成B集合。然后就可以愉快的连边啦。连完边后最多能放的船的数量就相当于跑一个最大匹配就可以啦。
贴上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct edge{
int next;
int to;
};
edge a[2505];
int t,n,m,k,nx,sum;
int x[55][55],y[55][55];
int h[1255],who[1255];
char c[55][55];
bool f[1255];
void build_x(int i){
bool flag=true;
for (int j=0;j<m;j++){
if (c[i][j]=='*'){
if (flag) { sum++; flag=false; }
x[i][j]=sum;
}
if (c[i][j]=='#')
flag=true;
}
}
void build_y(int i){
bool flag=true;
for (int j=0;j<n;j++){
if (c[j][i]=='*'){
if (flag) { sum++; flag=false; }
y[j][i]=sum;
}
if (c[j][i]=='#')
flag=true;
}
}
void read(int u,int v){
k++;
a[k].next=h[u];
a[k].to=v;
h[u]=k;
}
bool dfs(int i){
if (f[i])
return false;
f[i]=true;
for (int j=h[i];j;j=a[j].next){
if (!who[a[j].to]||dfs(who[a[j].to])){
who[a[j].to]=i;
return true;
}
}
return false;
}
int main(){
scanf("%d",&t);
while (t--){
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(h,0,sizeof(h));
memset(who,0,sizeof(who));
k=0;
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
scanf("%s",c[i]);
sum=0;
for (int i=0;i<n;i++)
build_x(i);
nx=sum;
sum=0;
for (int i=0;i<m;i++)
build_y(i);
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
if (x[i][j]!=0&&y[i][j]!=0)
read(x[i][j],y[i][j]);
int ans=0;
for (int i=1;i<=nx;i++){
memset(f,false,sizeof(f));
if (dfs(i))
ans++;
}
printf("%d\n",ans);
}
return 0;
}