题目链接
这道题用以理解匈牙利算法是一道很不错的题。先讲讲题意吧。
题意:有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;
}