题意: 每个雷达可以最多覆盖相邻两个城市* ,询问最少防止几个雷达
思路:
这种类型在 二分图建图方法这篇文章中有讲。因为只能覆盖2个,而这个2 便是建图的依据,枚举点的位置,是否可以同相邻四点建立边, 之后找出最大匹配。
雷达最少的数量,相当于一个点, 就是说用最少的点覆盖所有的城市, 便是一个最小点覆盖的问题:
最小点覆盖= 总点数 - 最大匹配。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include <vector>
#include<algorithm>
using namespace std;
const int maxn=1505;
int n,m;
char mp[maxn][maxn];
int pre[maxn];//保存各点的匹配点
int vis[maxn];
int voc[maxn];
int num[maxn][maxn];
vector<int> vec[maxn];
int find(int u)//判断是否存在增广路,存在返回1
{
int i,v;
for(i=0;i<vec[u].size();i++)
{
v=vec[u][i];
if(vis[v])continue;
vis[v]=1;
if(pre[v]==-1||find(pre[v]))//-1找到未盖点,find是增广路。
{
pre[v]=u;//匹配边和非匹配边交换
voc[v]=u;
voc[u]=v;
return 1;
}
}
return 0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(pre,-1,sizeof(pre));
memset(num,0,sizeof(num));
memset(mp,0,sizeof(mp));
for(int i=1;i<=n*m;i++)
vec[i].clear();
for(int i=1;i<=n;i++)
scanf("%s",&mp[i][1]);
int cnt=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
if(mp[i][j]=='*')
num[i][j]=++cnt;
}
int data[4][2]={0,1,0,-1,1,0,-1,0};
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j]=='*')
{
int xi,yi;
for(int k=0;k<4;k++)
{
xi=i+data[k][0],yi=j+data[k][1];
if(xi<1||xi>n||yi<1||yi>m||mp[i][j]!='*')continue;
vec[ num[i][j] ].push_back(num[xi][yi]);
}
}
}
}
memset(voc,-1,sizeof(voc));
int ans=0;
for(int i=1;i<=cnt;i++)
{
memset(vis,0,sizeof(vis));
ans+=find(i);
}
printf("%d\n",cnt-ans/2);
}
return 0;
}