Taxi Cab Scheme 【HDU - 1350】【匈牙利算法解决最小链覆盖】

题目链接


  这道题用以理解匈牙利算法是一道很不错的题。先讲讲题意吧。

  题意:有N个人,他们在特定的时间(hour : minute)要从地点(a, b)前往地点(c, d),然后呢,我们如果说前一个人i的出租车可以开回来再接j这个人,那么他们就可以少叫一辆出租车了。现在,题目想知道最少的需求的出租车的个数。

  思路:看到这样的问题的时候,很容易让人想到最小链覆盖等于最长反链这样一个性质,但是,再仔细想想看,它的反链的信息不是唯一确定的,而是通过两两之间固定的。那么,意味着我们不能在利用反链来维护了。

  所以,又由于这里的N比较的小,开始往匈牙利算法的匹配上去想了,那么可以看成是用总的点数减去匹配数,岂不就是我们需要的答案了嘛。关于匈牙利算法,可以先看看这篇博客来预习或者巩固一下。

  但是,很多时候我们会拿这个算法去解决二分图问题,现在我们可以说是一条链了。但是一样是可以用匈牙利算法来做的。

  譬如说,现在我们有5个点,序号1~5,有些点他们可能会存在一些可行解。也就是可以匹配的关系。

譬如说,我们用箭头来指向下一个可以继承它的关系的点。(白话文:如果i用完的出租车,j也能用,那么就用i指向j)

举个例子,形成了这样的一幅图,也就是说1号客人用完的出租车是可以给2~5位用的,同理2、3。

  那么,如果我们现在边的有向性是这样建立的,会发生什么呢?譬如说,我们1匹配上了5,但是呢2想去匹配5的时候,最好应该是让1的匹配换成2,但是呢,我们的5实际上是没有出边的,这样最后剩下5的时候岂不是没法更换了?

bool match(int u)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(vis[v]) continue;
        vis[v] = true;
        if(!link[v] || match(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}

  我们读到这个匈牙利算法的match(也就是交换的部分,试着让前者交换)。

  那么,我们现在可以把1指向2,把2指向3(这里的指向指的是“匹配”),这些都是没有问题的,但是呢,如果说,我们最后的5,实际上是做成匹配的呢?

譬如说是:

  我们可以很容易的会让1匹配上3,并且link[3] = 1;2匹配上4,link[4] = 2;但是这样的做法之后呢,就会让3、4没法匹配了,因为匈牙利算法的Hungery部分:

int Hungery()
{
    int ans = 0;
    for(int i=1; i<=N; i++) link[i] = 0;
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=N; j++) vis[j] = false;
        if(!link[i] && match(i)) ans++;
    }
    return ans;
}

  也就是说,如果已经link[i]了,就不需要再匹配下去了。

  所以,我们要尽可能的先匹配前面的,要先让前面的link[1]、link[2],……先赋上值。所以我们把边反向。这样就可以解决这个问题了。

  这样,我们就可以做到开始的匹配先将前面的值赋值了,不会影响到后面的答案了。

上图的样例:

1
4
00:00 0 0 0 1
00:03 0 0 1 1
00:07 1 2 2 2
00:09 2 2 3 3
ans:1
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 505;
int N, head[maxN], cnt, tot;
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN * maxN];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
struct node_Station
{
    int hour, minu, a, b, c, d;
    node_Station(int h=0, int m=0, int a=0, int b=0, int c=0, int d=0):hour(a), minu(b), a(a), b(b), c(c), d(d) {}
    inline void In_Put() { scanf("%d:%d%d%d%d%d", &hour, &minu, &a, &b, &c, &d); }
} a[maxN];
inline int Dis(node_Station e1, node_Station e2) { return abs(e1.a - e1.c) + abs(e1.b - e1.d) + abs(e1.c - e2.a) + abs(e1.d - e2.b); }
inline int Time_Det(node_Station e1, node_Station e2) { return 60 * (e2.hour - e1.hour) + e2.minu - e1.minu; }
int link[maxN];
bool vis[maxN * maxN];
bool match(int u)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(vis[v]) continue;
        vis[v] = true;
        if(!link[v] || match(link[v]))
        {
            link[v] = u;
            return true;
        }
    }
    return false;
}
int Hungery()
{
    int ans = 0;
    for(int i=1; i<=N; i++) link[i] = 0;
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=N; j++) vis[j] = false;
        if(!link[i] && match(i)) ans++;
    }
    return ans;
}
inline void init()
{
    cnt = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &N);
        init();
        for(int i=1; i<=N; i++) a[i].In_Put();
        for(int i=2; i<=N; i++)
        {
            for(int j=i-1; j; j--)
            {
                if(Time_Det(a[j], a[i]) > Dis(a[j], a[i]))
                    addEddge(i, j);
            }
        }
        printf("%d\n", N - Hungery());
    }
    return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值