题目大意:
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
- #include <stdio.h>
- #include <memory.h>
- #define clr(a) memset(a,0,sizeof(a))
- #define N 505
- int find(int i,int m,int g[][N],int mat[],int tmat[]){
- int v,j;
- for(j=0;j<m;j++){
- if(g[i][j]&&tmat[j]==0){
- tmat[j]=1; v=mat[j]; mat[j]=i;
- if(v==-1||find(v,m,g,mat,tmat)) return 1;
- mat[j]=v;
- }
- }
- return 0;
- }
- int match(int g[][N],int n,int m,int mat[]){
- int i,k=0,tmat[N];
- for(i=0;i<m;i++) mat[i]=-1;
- for(i=0;i<n;i++){
- clr(tmat); k+=find(i,m,g,mat,tmat);
- }
- return k;
- }
- int n,m;
- int a[N][N];
- int mat[N];
- int main()
- {
- int i,j,k;
-
- while(scanf("%d%d",&n,&m)!=EOF){
-
- clr(a);
-
- for(k=0;k<m;k++){
- scanf("%d%d",&i,&j);
- i--; j--;
- a[i][j]=1;
- }
-
- k=match(a,n,n,mat);
- printf("%d/n",k);
- }
-
- return 0;
- }