【Hopcroft-Karp && 二分图的最大匹配数 && 有向图】HDU - 2389 Rain on your Parade

Problem Description

给你时间Time。给你n个人,每个人给你所在位置和移动速度。给你m个雨伞的坐标。问你在Time时间内,最多有多少个人能得到雨伞。

思路:

暴力n*m求人与雨伞之间的距离,判断人在时间Time内能否到达雨伞处,如果能够到达建一条边。
然后就是最二分图的最大匹配,左边是人,右边是雨伞。
匈牙利算法超时。
得换一个快一点的算法Hopcroft-Karp
Hopcroft-Karp:
我们每次从所有未匹配的左边结点开始BFS, 进行距离的标号。对于每一个队列中的左边结点X, 考虑与它相邻的所有右边结点Y: 如果Y是一个未匹配的右边结点,则说明至少还存在一条增广路,就标记一下,以便之后的dfs(X)。否则,将Y的匹配结点加入到队列中。
dfs(X)过程中,只考虑这样的边(u,v):满足dx[u] + 1 = dy[to]

#include<bits/stdc++.h>
using namespace std;
#define nn 3005
struct node
{
    int x, y, s;
};
node num[nn], ub[nn];
vector<int> Map[nn];
int Mx[nn], My[nn], n;//Mx[i]表示左边点i,匹配到右边点Mx[i]。My[i]表示右边点i,匹配到左边点My[i]。
int dx[nn], dy[nn];//dx[i], dy[i]都表示i这个点到最开始入队列点的距离。求出dx[],dy[]bfs关系,用来约束dfs搜索
bool vis[nn];
//Hopcroft-Karp
bool dfs(int u)
{
    for(int i = 0; i < Map[u].size(); i++)
    {
        int to = Map[u][i];
        if(!vis[to] && dy[to] == dx[u] + 1)//根据dy[to]==dx[u]+1,进行搜索。可以节省时间
        {
            vis[to] = 1;
            if(!My[to] || dfs(My[to]))
            {
                Mx[u] = to;
                My[to] = u;
                return 1;
            }
        }
    }
    return 0;
}
bool bfs()
{
    queue<int> q;
    bool flag = 0;
    memset(dx, 0, sizeof(dx));
    memset(dy, 0, sizeof(dy));
    for(int i = 1; i <= n; i++)//没有匹配的点,入队列
        if(!Mx[i])
            q.push(i);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i = 0; i < Map[u].size(); i++)
        {
            int to = Map[u][i];
            if(!dy[to])
            {
                dy[to] = dx[u] + 1;
                if(!My[to]) flag = 1;//至少还存在一条增广路,标记一下
                else
                {
                   dx[My[to]] = dy[to] + 1;
                   q.push(My[to]);
                }
            }
        }
    }
    return flag;
}
int solve()
{
    memset(My, 0, sizeof(My));
    memset(Mx, 0, sizeof(Mx));
    int ans = 0;
    while(bfs())
    {
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++)
            if(!Mx[i] && dfs(i))
                ans++;
    }
    return ans;
}
//
double dist(node x, node y)//两点间距离公式
{
    return sqrt((x.x-y.x)*(x.x-y.x) + (x.y-y.y)*(x.y-y.y));
}
int main()
{
    int T, Case = 1, i, j, Time, m;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &Time);
        scanf("%d", &n);
        for(i = 1; i <= n; i++)
        {
            scanf("%d %d %d", &num[i].x, &num[i].y, &num[i].s);
            Map[i].clear();
        }
        scanf("%d", &m);
        for(i = 1; i <= m; i++)
            scanf("%d %d", &ub[i].x, &ub[i].y);
        for(i = 1; i <= n; i++)
            for(j = 1; j <= m; j++)
                if(dist(num[i], ub[j]) <= Time*num[i].s)//在规定时间内能到达的建边
                {
                     Map[i].push_back(j);//单向边
                }
        printf("Scenario #%d:\n", Case++);
        printf("%d\n\n", solve());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值