poj 3041二分匹配

题意:矩阵上有一些小行星,占据了一些格子,我们每次操作可以清理一列中的所有小行星,也可以清理一行中的所有小行星,问最少进行多少次操作可以清理掉所有的小行星。

分析:建图之后,会发现,题目意思就等于:最小覆盖点,

最小覆盖点 = 最大匹配;

问题就 = 求二分图的最大匹配数。

 

对于匈牙利算法有两种形式:

1、bfs  时间32ms


//32ms

#include
#include
using namespace std;

 
const int MAXN = 520;
  int g[MAXN][MAXN], Mx[MAXN], My[MAXN], Nx,Ny;
  int chk[MAXN], Q[MAXN], prev[MAXN];
  int MaxMatch(void)
  {
     int res = 0;
     int qs, qe;
     memset(Mx, -1, sizeof(Mx));
     memset(My, -1, sizeof(My));
     memset(chk, -1, sizeof(chk));
     for (int i = 1; i <= Nx; i++)
   {
         if (Mx[i] == -1)
   {//对于x集合中的每个没有匹配的点i进行一次bfs找交错轨
             qs = qe = 0;
             Q[qe++] = i;
             prev[i] = -1;
 
             bool flag = 0;//判断是否找到
             while (qs < qe && !flag)
    {
                 int u = Q[qs];
                 for (int v = 1; v <= Nx && !flag; v++)
                     if (g[u][v]//如果u和v相连
        &&chk[v] != i)//并且v没有被u check过
                         {
                         chk[v] = i;
                         Q[qe++] = My[v];//放进
                         if (My[v] >= 0)//如果v和其他的相连,则修改之
                             prev[My[v]] = u;
                         else
       {//直到找到一个u和v都没有用过的
                             flag = 1;
                             int d = u, e = v;
                             while (d != -1)
        {//确保回到最初
                                 int t = Mx[d];
                                 Mx[d] = e;
                                 My[e] = d;
                                 d = prev[d];
                                 e = t;
                             }
                         }
                     }
                 qs++;
             }
             if (Mx[i] != -1)
                 res++;
         }
     }
     return res;
  }


  int main()
  {
   cin>>Nx>>Ny;
   memset(g , 0 ,sizeof(g));
   int i , x , y;
   for(i = 1 ; i <= Ny ;i++)
   {
    scanf("%d%d" , &x , &y);
    g[x][y] = 1;
   
   }
   int sum = MaxMatch();
  cout<<sum<<endl;
   return 0;
  }


 

2、dfs 时间 16ms

#include
#include
using namespace std;

const int MAX = 520 ;

int grap[MAX][MAX] , link[MAX] , useif[MAX] ;
int n , m;

int can(int t)
{
 int i;
 for(i = 1 ; i <= n ; i++)
 {
  if(grap[t][i] &&!useif[i])
  {
   useif[i] =1;
   if(link[i] ==-1 || can(link[i]))
   {
    link[i]= t;
    return1;
   }
  }
 }
 return 0;
}

int main()
{
 scanf("%d %d" , &n , &m);
 int i , x , y;
 memset(grap , 0 , sizeof(grap));
 for(i = 1 ; i <= m ; i++)
 {
  scanf("%d %d" , &x ,&y);
  grap[x][y] = 1;
 }
 memset(link , -1 , sizeof(link));
 int sum = 0;
 for(i = 1 ; i <= n ; i++)
 {
  memset(useif , 0 ,sizeof(useif));
  if(can(i))
   sum +=1;
 }
 printf("%d\n" , sum);
 return 0;
}  //16ms


 

本来两种形式算法的效率的差不多的,但在这个题目中相差一倍,可能是测试数据的原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值