HDU2389---Rain on your Parade (二分图匹配-HK算法)

题目来源:https://vjudge.net/problem/HDU-2389

题意

在一个露天party里,宾客们在狂欢,还有ts时间将要下雨,现场有m个人,地上有n个雨伞,各自有着不同的坐标,人有着各自的奔跑速度,一个伞只能够容纳一个人,问, 有多少个人可以不被雨淋湿。

思路

m个人,n把伞,典型的匹配问题,只要能跑到的就说明人和该伞有关系,然后就是二分匹配,但是普通的二分匹配会超时(时间复杂度是O(VE)),所以要用到二分匹配的HK算法(同时找到多条增广轨,时间复杂度O(V^0.5*E)),来优化整个匹配过程。
比较好的博客(HK算法):
http://files.cnblogs.com/files/liuxin1.pdf

代码

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const double eps=1e-6;
int m,n,limit;
bool mp[3000+10][3000+10];
bool vis[3000+10];
int linkX[3000+10],linkY[3000+10];//记录该点已经被谁连接了
int depthX[3000+10],depthY[3000+10];//记录下每一个点的深度
struct people
{
    double x,y,v;
} p[3000+10];
struct un
{
    double x,y;
} s[3000+10];
double dis(double x,double y,double x2,double y2)
{
    return sqrt((x-x2)*(x-x2)+(y-y2)*(y-y2));
}
bool seachAP()
{
    limit=INF;
    queue<int> q;
    memset(depthX,-1,sizeof(depthX));
    memset(depthY,-1,sizeof(depthY));
    for(int i=1; i<=m; i++)
    {
        if(linkX[i]==-1)//把尚未匹配的假如队列,一个一个用深度标记之后,y利用dfs一起匹配,这是优化的地方
        {
            q.push(i);
            depthX[i]=0;
        }
    }
    while(!q.empty())
    {
        int w=q.front();
        q.pop();
        if(depthX[w]>limit) break;//如果找到了能够匹配的,就退出,但是有人会说为啥不在找到的时候直接退出,因为要标记所有长度深度不大于limit的组合
        for(int i=1; i<=n; i++)
        {
            if(mp[w][i]&&depthY[i]==-1)
            {
                depthY[i]=depthX[w]+1;
                if(linkY[i]==-1)
                {
                    limit=depthY[i];//limit赋值
                }
                else
                {
                    depthX[linkY[i]]=depthY[i]+1;
                    q.push(linkY[i]);//增广路
                }
            }
        }
    }
    return limit!=INF;

}
bool dfs(int x)
{
    for(int i=1; i<=n; i++)
    {
        if(!vis[i]&&mp[x][i]&&depthY[i]==depthX[x]+1)
        {
            vis[i]=1;
            if(linkY[i]!=-1&&depthY[i]==limit) continue;
            if(linkY[i]==-1||dfs(linkY[i]))
            {
                linkX[x]=i;
                linkY[i]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int T,cases=1;
    scanf("%d",&T);
    while(T--)
    {
        double t;
        scanf("%lf",&t);
        scanf("%d",&m);
        for(int i=1; i<=m; i++)
        {
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].v);
        }
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lf%lf",&s[i].x,&s[i].y);
        }
        memset(mp,0,sizeof(mp));
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=n; j++)
            {
                double d=dis(p[i].x,p[i].y,s[j].x,s[j].y);
                if(d/p[i].v-t<eps)
                {
                    mp[i][j]=1;
                }
            }
        }
        int res=0;
        memset(linkX,-1,sizeof(linkX));
        memset(linkY,-1,sizeof(linkY));
        while(seachAP())
        {
            memset(vis,0,sizeof(vis));
            for(int i=1; i<=m; i++)
            {
                if(linkX[i]==-1&&dfs(i))
                {
                    res++;
                }
            }
        }  
        printf("Scenario #%d:\n",cases++);
        printf("%d\n\n",res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值