POJ 3020 Antenna Placement(无向图最小边覆盖)

poj 3020

题目大意

一个矩形中,有N个城市’*’,现在这n个城市都要覆盖无线,若放置一个基站,那么它至多可以覆盖相邻的两个城市。问至少放置多少个基站才能使得所有的城市都覆盖无线?

分析

在这道题上卡了很久,才接触二分图也没什么好的思路。这道题需要用到一个定理:

二分图最小边覆盖 = 两边顶点数 - 最大匹配数
无向图的最小边覆盖 = (二分图两边顶点数 - 二分图的最大匹配数)/2.

下面列举几个二分图问题的常用定理:

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:二分图最小边覆盖 = 顶点数 - 最大匹配数
定理3:最大匹配数 = 最大独立数

这些定理是需要掌握的基础,但问题的关键是如何将要解决的问题归约成我们熟悉的模型。也就是如何建图的问题。
基站将两个城市覆盖可以看成是在两个城市间连了一条边,这样问题变成了一个图论问题,对于图中的每一个联通分量,至少用多少边才能将所有点都覆盖,这就是比较典型的无向图最小边覆盖问题。

接下来讲如何将无向图最小边覆盖问题转化为二分图的最小边覆盖问题的。

比如原来的无向图是这样的:
这里写图片描述
这里经过一个叫做拆点的操作,其实就是将原图复制了一份,显而易见,拆点后图的最小边覆盖数是原图的两倍。注意观察里面点的命名,一条边总是连接一个实点和一个虚点(二分图一条边总是连接左边的点后右边的点)
这里写图片描述
实际上经过拆点之后图变成了一个二分图(只是重排了一下点的位置,但它们之间的拓扑关系不变)
这里写图片描述
这个二分图和拆点后的无向图的最小边覆盖数是相等的,所以也是原无向图的两倍。

注意

用以上方法思考之后我产生一个问题,对于那些孤立的城市(旁边没有其他城市),它也需要一个基站来覆盖,但在无向图的建图中是没有边的,为什么以上程序还能正常运行呢?

仔细想了想后发现其实这是没有问题的,因为对于孤立的点这个联通分量,也是满足:
二分图最小边覆盖 = 两边顶点数 - 最大匹配数=2
无向图的最小边覆盖 = (二分图两边顶点数 - 二分图的最大匹配数)/2=1
是没有问题的。

不知道很多博客里为什么是最小路径覆盖

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
int T;
int B,A;
int grid[100][100];
int find_node[100][100];
int mat[500][500];//储存二分图的边
int link[500];//与y节点相配对的x节点
int visit[600];//y节点是否被配对了
int nodecount=0;
bool Can(int t)
{
     for(int i=1;i<=nodecount;i++)
     {
           if(mat[t][i]==1 && visit[i]==0)
           {
                visit[i]=1;
                if(link[i]==-1 || Can(link[i]))
                {
                    link[i]=t;
                    return 1;
                }
           }
     }
     return 0;
}
int Match()
{
      int ans=0;
      for(int i=1;i<=nodecount;i++)
      {
            memset(visit,0,sizeof(visit));
            if(Can(i))ans++;
      }
      return ans;
}
void Build()
{
      for(int j=B;j>=1;j--)
      {
            for(int i=1;i<=A;i++)
            {
                  if(grid[i][j]==1)
                  {
                      if(grid[i+1][j]==1){mat[find_node[i][j]][find_node[i+1][j]]=1;mat[find_node[i+1][j]][find_node[i][j]]=1;}
                      if(grid[i][j-1]==1){mat[find_node[i][j-1]][find_node[i][j]]=1;mat[find_node[i][j]][find_node[i][j-1]]=1;}
                  }
            }
      }
}
int main()
{
     scanf("%d",&T);
     char c;
     while(T--)
     {
           nodecount=0;
           memset(mat,0,sizeof(mat));
           memset(grid,0,sizeof(grid));
           memset(link,-1,sizeof(link));
           scanf("%d%d",&B,&A);
           for(int j=B;j>=1;j--)
           {
               getchar();
               for(int i=1;i<=A;i++)
               {
                    scanf("%c",&c);
                    if(c=='*')
                    {
                          find_node[i][j]=++nodecount;
                    }
                    grid[i][j]= c=='*' ? 1: 0;
               }
           }
           Build();
           cout<<(nodecount*2-Match())/2<<endl;
     }
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值