poj 1719最大二分匹配hungary算法

首先在这道题之前先说明几个简单的概念

二分图:如果一个图G<V,E>,V能够划分成为两个子集,V1,V2,而这两个子集中的点两两不相邻,则这个图称为二分图

匹配:E的一个子集E1,使得V1中的点至多只与V2中的一个点相邻,则我们可以称为G1<V,E1>,则这个子图为E的一个匹配

最大匹配:如果一个匹配所包含的边最多,则称为最大匹配.

Hungary算法:图论中一种计算最大匹配的算法,其原理是遍历图中的V1每个结点,看他们是否能够增广进子图中,判断采用dfs,即对于一个点V1,找到与其相邻的一个点,看其是否有匹配,若没有则直接匹配,若有则拆点继续深度搜索.可以看出其复杂度为O(V^2E)

其伪代码为:


void hungary(G)
{
     sum = 0
     for every V in V1
         if(dfs(V))sum++//如果能够增广,匹配数加1
}
bool dfs(int v1)//判断点是否能够增广
{
    for every U in V2
         u.check = false
    for every U in V2
         if(<v1,u> && !u.check)
            u.check = true
            if(!u.match || dfs(u.match)))//如果u尚未匹配或可以拆点后
            {                            //更改匹配,就可以改变匹配
                 u.match = v1
                 return true
            }
    return false
}

http://poj.org/problem?id=1719

下面这一题的大意是这样的:一个射击靶每一列两个白色格子,现在要求每一列正好击中一个白色格子,且每一行都有白色格子被击中,问是否能够完成这样一个匹配.

显然,我们用每一列匹配每一行,但要注意r<c,所以用hungary算法后可能会有某些列没有得到匹配,如果此时每一行都有了匹配,则随机选择一个白色的格子进行匹配即可.(开始没有注意到贡献了1WA)

附上源代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define V 1010
using namespace std;
int graph[V][V] , mat[V] , row , column;
bool chk[V];
bool dfs(int p)
{
        for(int i = 1; i <= column ; i++)
        {
                if(!chk[i]&&graph[p][i])
                {
                         chk[i] = true;
                         if(mat[i] == 0 || dfs(mat[i]))
                         {
                                 mat[i] = p;
                                 return true;
                         }
                }
        }
        return false;
}
void hungary()
{
         int ans = 0;
         for(int i = 1;i <= row ; i++)
         {
                  memset(chk , false , sizeof(chk));
                  if(dfs(i))ans++;
         }
         if(ans == row)
         {
                   for(int i = 1; i <=column ; i++)
                   {
                            if(mat[i] != 0)cout << mat[i] << " ";
                            else
                            {
                                     for(int j = 1; j <=row ; j++)
                                     {
                                            if(graph[j][i] == 1)
                                            {
                                                   cout << j << " ";
                                                   break;
                                            }
                                     }
                            }
                   }
                   cout << endl;
         }
         else cout << "NO" << endl;
         return;
}
int main()
{
         freopen("input" , "r" , stdin);
         int bl_num;
         cin >> bl_num;
         while(bl_num --)
         {
                 memset(graph,0,sizeof(graph));
                 memset(mat,0,sizeof(mat));
                 cin >> row >> column;
                 int temp1 , temp2;
                 for(int i = 1; i <=column ; i++)
                 {
                         cin >> temp1 >> temp2;
                         graph[temp1][i] = graph[temp2][i] = 1;
                 }
                 hungary();
         }
         return 0;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值