题意:需要求最大的放置炮台数(每个炮台都不能在其他炮台攻击范围内)。
炮台攻击范围是:该炮台所在的行和列,但是‘X'可以阻挡炮台攻击。
思路:二分匹配必须要有两个互不关联的集合,所以可以将行和列看做两个集合,但是’X'可以阻挡攻击,即若一行中有‘X'则该行可能放2个炮台。所以,我们将每行和每列相连的’ . '作为一个点(标号一致),这样按照行或列形成的图中标号的个数即为各集合的点数,若两者有共同的区域(格子,为' . ')则建立边。
#include<iostream>
#include<string.h>
using namespace std;
char map[20][20];
int aa[20][20] , bb[20][20] , cc[20][20];
int pp[50], vis[20];
int n;
int find_match(int u , int b)
{
for(int i=1;i<=b;i++)
{
if(cc[u][i]==1 && vis[i]==0)
{
vis[i]=1;
if(pp[20+i]==0 || find_match(pp[20+i] , b))
{
pp[20+i] = u; pp[u]=i;
return 1;
}
}
}
return 0;
}
int find_path(int a , int b)
{
int match = 0;
memset(pp,0 ,sizeof(pp));
for(int i=1;i<=a;i++)
{
if(pp[i]==0)
{
memset(vis,0,sizeof(vis));
match+= find_match(i,b);
}
}
return match;
}
int main()
{
while(scanf("%d",&n)!=EOF && n)
{
memset(aa,0,sizeof(aa));
memset(bb,0,sizeof(bb));
memset(cc,0,sizeof(cc));
for(int i=1;i<=n;i++) scanf("%s",map[i]);
//以“行”建图
int a=1 , flag = 0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<n;j++)
{
while(map[i][j]=='.'&&j<n) flag=1 , aa[i][j+1]=a , j++;
if(flag==1) a++ , j--; flag=0;
/* while(map[i][j]=='X'&&j<n)
{
if(flag==0) a++ , flag=1; j++;
}
if(map[i][j]=='\0')
{if(flag==0) flag=1 ,a++; j++;}*/
}
}/* cout<<"a== "<<a<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) cout<<aa[i][j]<<" "; cout<<endl;
}*/
//以”列“建图
int b=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<n;j++)
{
int t=i;
while(map[t][j]=='.'&&t<=n&&bb[t][j+1]==0) { flag=1 , bb[t][j+1]=b , t++;}
if(flag==1) b++ ; flag = 0;
}
}//cout<<"b= "<<b<<endl;
// 建”边 “
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(map[i][j-1]=='.') cc[aa[i][j]][bb[i][j]] = cc[bb[i][j]][aa[i][j]] = 1;
}
}
/*for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) cout<<bb[i][j]<<" "; cout<<endl;
}*/
printf("%d\n",find_path(a-1 ,b-1));
}
return 0;
}