PKU 3041 Asteroids - 行列的二分图模型 最小点覆盖

题目大意:

N*N的空间中有K个小行星,使用超级武器可以一次性清理一行或一列的所有小行星.但是超级武器很贵,给出星空图,问最少需要使用多少次武器能清除所有的小行星.N<500, k<10,000

分析:

很经典的行列模型转换为二分图模型来处理的题目,ZJU1002和PKU2226也是可以这样转换.

将行和列分别作为二分图左边和右边的节点.若在地图(i,j)上有一颗小行星,那么二分图左边i节点和右边j节点之间连一条边.

将此模型对应到题目上来,使用一次武器就是选择二分图上左边或右边的一个点v.和v相连的边,表示v行(或列)当中所有的小行星.那么题目的问题转换为了:在二分图中选择最少的点,使得所有的边都至少有一个端点被覆盖到.

这就是经典的二分图的最小点覆盖.

根据König定理:二分图 最小点覆盖数 = 最大匹配数.

所以题目转换为了求二分图的最大匹配,也就比较完美的解决了.

顺便说一句,ZJU1002的二分图模型的不能直接用行或列作为二分图的点,而是需要拆点.详细请看我的另一篇解题报告

http://blog.csdn.net/tiaotiaoyly/archive/2008/10/23/3122893.aspx

 

  1. /*
  2. PKU3041 Asteroids
  3. */
  4. #include <stdio.h>
  5. #include <memory.h>
  6. #define clr(a) memset(a,0,sizeof(a))
  7. #define N 505
  8. int find(int i,int m,int g[][N],int mat[],int tmat[]){
  9.     int v,j;
  10.     for(j=0;j<m;j++){
  11.         if(g[i][j]&&tmat[j]==0){
  12.             tmat[j]=1; v=mat[j]; mat[j]=i;
  13.             if(v==-1||find(v,m,g,mat,tmat)) return 1;
  14.             mat[j]=v;
  15.         }
  16.     }
  17.     return 0;
  18. }
  19. int match(int g[][N],int n,int m,int mat[]){
  20.     int i,k=0,tmat[N];
  21.     for(i=0;i<m;i++) mat[i]=-1;
  22.     for(i=0;i<n;i++){
  23.         clr(tmat); k+=find(i,m,g,mat,tmat);
  24.     }
  25.     return k;
  26. }
  27. int n,m;
  28. int a[N][N];
  29. int mat[N];
  30. int main()
  31. {
  32.     int i,j,k;
  33.     
  34.     while(scanf("%d%d",&n,&m)!=EOF){
  35.         //init
  36.         clr(a);
  37.         //input
  38.         for(k=0;k<m;k++){
  39.             scanf("%d%d",&i,&j);
  40.             i--; j--;
  41.             a[i][j]=1;
  42.         }
  43.         //output
  44.         k=match(a,n,n,mat);
  45.         printf("%d/n",k);
  46.     }
  47.     
  48.     return 0;
  49. }

 

你可能感兴趣的:(ini)