poj3020 Antenna Placement 题解
题目传送门
题目大意:给出一个包含 *
和 o
的矩阵,其中 *
代表城市 o
代表空地。一个网络基站可以覆盖相邻的两个地方,现在要让所有的城市都覆盖网络,求至少要几个基站。
显然能以 \(x\) 坐标和 \(y\) 坐标来作为节点,我们需要把每个城市作为节点,然后相邻的城市建一条双向边。
但是我们发现这样不是二分图,所以我们就要拆点,比如说这张图存在两个无向边 \((1,2)\) 和 \((2,3)\) ,就是这样:
然后就可以直接求最小路径覆盖就可以了。
根据公式 无向二分图的最小路径覆盖 \(=\) 顶点数 \(-\) 最大匹配数 \(\div 2\) 就可以直接求出答案了。
注意定点数是拆点之前的顶点数。
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#define maxt 139
#define maxn 1039
#define maxm 1000039
#define INF 0x7fffffff
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
//#define debug
typedef int Type;
inline Type read(){
Type sum=0;
int flag=0;
char c=getchar();
while((c<'0'||c>'9')&&c!='-') c=getchar();
if(c=='-') c=getchar(),flag=1;
while('0'<=c&&c<='9'){
sum=(sum<<1)+(sum<<3)+(c^48);
c=getchar();
}
if(flag) return -sum;
return sum;
}
int fx[5]={0,0,1,-1},fy[5]={1,-1,0,0};
int n,m,cnt,c,tox,toy;
int map[maxt][maxt],f[maxn][maxn];
int s,t,ans,dis[maxn],now[maxn];
int head[maxn],to[maxm],nex[maxm],w[maxm],kkk;
#define add(x,y,z) nex[++kkk]=head[x]; head[x]=kkk; to[kkk]=y; w[kkk]=z;
queue<int> q,E;
int bfs(){
memset(dis,0,sizeof(dis)); q=E; q.push(s);
dis[s]=1; for(int i=1;i<=cnt;i++) now[i]=head[i];
while(!q.empty()){
int cur=q.front(); q.pop();
for(int i=head[cur];i;i=nex[i])
if(!dis[to[i]]&&w[i]>0){
dis[to[i]]=dis[cur]+1; q.push(to[i]);
if(to[i]==t) return 1;
}
}
return 0;
}
int dfs(int x,int sum){
if(x==t) return sum; int res=0,tmp;
for(int i=now[x];i&&sum>0;i=nex[i]){
now[x]=i;
if(dis[x]+1==dis[to[i]]&&w[i]>0){
tmp=dfs( to[i],min(w[i],sum) );
if(tmp==0) dis[to[i]]=0;
w[i]-=tmp; w[i^1]+=tmp; sum-=tmp; res+=tmp;
}
if(!sum) break;
}
return res;
}
void work(){
n=read(); m=read(); cnt=0; kkk=1; memset(head,0,sizeof(head)); memset(f,0,sizeof(f));
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
c=getchar(); while(c!='o'&&c!='*') c=getchar();
if(c=='o') map[i][j]=0;
else map[i][j]=++cnt;
}
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
if(map[i][j]){
for(int x=0;x<4;x++){
tox=i+fx[x]; toy=j+fy[x];
if(tox<1||tox>n||toy<1||toy>m) continue;
f[map[i][j]][map[tox][toy]]=1;
}
}
s=(cnt<<1)+1; t=(cnt<<1)+2;
for(int i=1;i<=cnt;i++){ add(s,i,1); add(i,s,0); add(i+cnt,t,1); add(t,i+cnt,0); }
for(int i=1;i<=cnt;i++) for(int j=i+1;j<=cnt;j++)
if(f[i][j]){ add(i,j+cnt,1); add(j+cnt,i,0) add(j,i+cnt,1); add(i+cnt,j,0) }
cnt=(cnt<<1)+2; ans=0; while(bfs()) ans+=dfs(s,0x7fffffff);
printf("%d\n",((cnt-2)>>1)-ans/2);
return;
}
int main(){
//freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
int T=read(); while(T--) work();
return 0;
}