二分图最大匹配Hopcroft_Karp算法 及hdu2389

            学二分好几天了。。以为匈牙利和KM算法够用的。。没想到我还是太天真了。晚上在切hdu 2389的时候匈牙利一直TLE。好吧!只能去网上看大牛的题解。所以就认识了Hopcroft_Karp算法(简称HK),一个二分匹配中很高效的算法。我们知道匈牙利算法复杂度为O(en),而HK为v(en^0.5),还是很快的。

          大概思路:用BFS对X部分的所有未被覆盖的点进行寻找增广路,列出所有的可行增广路,进行距离标号维护,即disy(v)=disx(u)+1;再用类似匈牙利算法中的DFS对未被匹配的点进行匹配,在判断是否进行增广时,进行条件判断,if(disy(v)==disx(u)+1)说明v是u的后继结点,然后进行匹配,匹配成功则计数加一。

          以下是代码,菜鸟只能用大牛代码了T T:

         

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<algorithm>
#define maxn 3005
#define sqr(x) ((x)*(x))
#define clr(a,b) memset(a,b,sizeof(a))

using namespace std;
int first[maxn],next[maxn*maxn],ev[maxn*maxn],e;   //邻接表存储,你懂的   
int cx[maxn],cy[maxn];                             //分别记录x,y部分的匹配
int n,m,ca,disx[maxn],disy[maxn];                  //距离标号
void addedge(int u,int v)
{
    next[e]=first[u];ev[e]=v;first[u]=e++;
}
bool find(int x)
{
    for(int e=first[x];e!=-1;e=next[e])
    {
        int v=ev[e];
        if(disy[v]==disx[x]+1)                 //标号距离判断,表示v是x增广路上的后继结点
        {
           disy[v]=0;                         //表示v访问过了,也可以用vis数组标记
           if(cy[v]==-1||find(cy[v]))
           {
               cx[x]=v;
               cy[v]=x;
               return 1;
           }
        }
    }
    return 0;
}
bool bfs()
{
    int flag=0;
    clr(disx,0);
    clr(disy,0);
    queue<int>q;
    for(int k=1;k<=n;k++)
       if(cx[k]==-1)             //对所有未覆盖的x部分的点加入队列
         q.push(k);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int e=first[u];e!=-1;e=next[e])
        {
            int v=ev[e];
            if(!disy[v])
            {
                disy[v]=disx[u]+1;       //标号维护
                if(cy[v]==-1) flag=1;    //增广路上存在未匹配的点
                else
                {
                    disx[cy[v]]=disy[v]+1;     //已匹配则增长增广路
                    q.push(cy[v]);
                }
            }
        }
    }
    return flag;
}
int HK()
{
    int Max=0;
    while(bfs())                     
    {
        for(int i=1;i<=n;i++)
           if(cx[i]==-1&&find(i))    //对未匹配的点寻找增广路进行匹配
                Max++;
    }
    return Max;
}

HDU 2389:  思路:建图:在规定时间内可以到达雨伞地点的标号连一条边。然后调用HK算法就ok了。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<algorithm>
#define maxn 3005
#define sqr(x) ((x)*(x))
#define clr(a,b) memset(a,b,sizeof(a))

using namespace std;
double px[maxn],py[maxn],s[maxn],ux[maxn],uy[maxn],t;
int first[maxn],next[maxn*maxn],ev[maxn*maxn],e;
int cx[maxn],cy[maxn];
int n,m,ca,disx[maxn],disy[maxn];
void addedge(int u,int v)
{
    next[e]=first[u];ev[e]=v;first[u]=e++;
}
double dis(double x1,double y1,double x2,double y2)
{
    return sqrt(sqr(x1-x2)+sqr(y1-y2));
}
bool find(int x)
{
    for(int e=first[x];e!=-1;e=next[e])
    {
        int v=ev[e];
        if(disy[v]==disx[x]+1)
        {
           disy[v]=0;
           if(cy[v]==-1||find(cy[v]))
           {
               cx[x]=v;
               cy[v]=x;
               return 1;
           }
        }
    }
    return 0;
}
bool bfs()
{
    int flag=0;
    clr(disx,0);
    clr(disy,0);
    queue<int>q;
    for(int k=1;k<=n;k++)
       if(cx[k]==-1)
         q.push(k);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int e=first[u];e!=-1;e=next[e])
        {
            int v=ev[e];
            if(!disy[v])
            {
                disy[v]=disx[u]+1;
                if(cy[v]==-1) flag=1;
                else
                {
                    disx[cy[v]]=disy[v]+1;
                    q.push(cy[v]);
                }
            }
        }
    }
    return flag;
}
int HK()
{
    int Max=0;
    while(bfs())
    {
        for(int i=1;i<=n;i++)
           if(cx[i]==-1&&find(i))
                Max++;
    }
    return Max;
}
void makemap()
{
    e=1;
    clr(cx,-1);
    clr(cy,-1);
    clr(first,-1);
    for(int i=1;i<=n;i++)
       for(int j=1;j<=m;j++)
       {
           if(dis(px[i],py[i],ux[j],uy[j])<=t*s[i])
                addedge(i,j);
       }
}
int main()
{
    freopen("D:/d.txt","r",stdin);
    scanf("%d",&ca);
    int cas=1;
    while(ca--)
    {
        scanf("%lf",&t);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
           scanf("%lf %lf %lf",&px[i],&py[i],&s[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
           scanf("%lf %lf",&ux[i],&uy[i]);
        makemap();
        printf("Scenario #%d:\n%d\n\n",cas++,HK());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值