poj3041

来源:互联网 发布:三菱plc编程软件cpu 编辑:程序博客网 时间:2024/05/16 06:04
 

转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1299322465

 

解题思路:

把方阵看做一个特殊的二分图(以行列分别作为两个顶点集V1、V2,其中| V1|=| V2|)

然后把每行x或者每列y看成一个点,而障碍物(x,y)可以看做连接x和y的边。按照这种思路构图后。问题就转化成为选择最少的一些点(x或y),使得从这些点与所有的边相邻,其实这就是最小点覆盖问题。

 

再利用二分图最大匹配的König定理:

最小点覆盖数 = 最大匹配数

 

(PS:最小点覆盖:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖图的所有的边。)

 

因此本题自然转化为求 二分图的最大匹配 问题

 

 

求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。但是这个算法的时间复杂度为边数的指数级函数。

因此,需要寻求一种更加高效的算法——用增广路求最大匹配的方法(匈牙利算法)

 

增广路的定义(也称增广轨或交错轨):

若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。

 

 由增广路的定义可以推出下述三个结论:

  1、P的路径个数必定为奇数,第一条边和最后一条边都不属于M。

2、将M和P进行取反操作可以得到一个更大的匹配M’

   (反操作:把P中的 匹配边 与 非匹配边 互换)

3、M为G的最大匹配当且仅当不存在M的增广路径P

 

 匈牙利算法轮廓:

  (1)置M为空

  (2)找出一条增广路径P,通过异或操作获得更大的匹配M’代替M

(3)重复(2)操作直到找不出增广路径为止

 

 

[cpp] view plain copy
print?
  1. //Memory Time   
  2. //464K   47MS   
  3.   
  4. #include<iostream>  
  5. using namespace std;  
  6.   
  7. int n,k;  //n矩阵规格,k星体数量  
  8. int V1,V2;       //二分图顶点集  
  9.  /*矩阵的行列分别属于二分图的两个顶点集V1、V2,其中行x∈V1,列y∈V2*/  
  10. bool grid[501][501];  //存储数据方式:可达矩阵  
  11. bool vis[501];     //记录V2的点每个点是否已被搜索过  
  12. int link[501];     //记录 V2中的点y 在 V1中 所匹配的点x的编号  
  13. int m;  //最大匹配数  
  14.   
  15. /*Hungary Algorithm*/  
  16.   
  17. bool dfs(int x)  
  18. {  
  19.     for(int y=1;y<=V2;y++)  
  20.         if(grid[x][y] && !vis[y])  //x到y相邻(有边) 且 节点y未被搜索  
  21.         {  
  22.             vis[y]=true;   //标记节点y已被搜索  
  23.             if(link[y]==0 || dfs(link[y])) //link[y]==0 : 如果y不属于前一个匹配M  
  24.             {                               //find(link[y] : 如果被y匹配到的节点可以寻找到增广路  
  25.                 link[y]=x; //那么可以更新匹配M'(用M替代M')  
  26.                 return true;  //返回匹配成功的标志  
  27.             }  
  28.         }  
  29.     return false;  //继续查找V1下一个x的邻接节点  
  30. }  
  31.   
  32. void search(void)  
  33. {  
  34.     for(int x=1;x<=V1;x++)  
  35.     {  
  36.         memset(vis,false,sizeof(vis)); //清空上次搜索时的标记  
  37.         if(dfs(x))  //从V1中的节点x开始寻找增广路  
  38.             m++;  
  39.     }  
  40.     return;  
  41. }  
  42.   
  43. int main(void)  
  44. {  
  45.     cin>>n>>k;  
  46.     V1=V2=n;  
  47.   
  48.     int x,y;         //坐标(临时变量)  
  49.     for(int i=1;i<=k;i++)  
  50.     {  
  51.         cin>>x>>y;  
  52.         grid[x][y]=true;   //相邻节点标记  
  53.     }  
  54.   
  55.     /*增广轨搜索*/  
  56.   
  57.     search();  
  58.   
  59.     /*Output*/  
  60.   
  61.     cout<<m<<endl;  
  62.   
  63.     return 0;  
  64. }  


原创粉丝点击