传送门
题解:
构造一个DAG,每个客人是一个节点,如果同一个出租车在接完客人u以后还来得及接客人v,那么连有向边(u,v)。
关于求DAG最小路径覆盖:
以下内容转自XWL的博客
我们可以发现: 在DAG中,每条路径除了结尾的点,其他每个点都有一个唯一的后继结点。
举个例子:上图中 2->3->4 这条路径上,2的后继3、3的后继是4、4没有后继。
那么求路径条数的问题可以转换为求结尾结点的个数。
1.我们把DAG中的每个点 u 拆分成两个点u和u’把 u放入二分图的X集合,u’放入Y集合。
2.如果u->v有一条边,那么在二分图中连接u->v’。
上面左图 ,按照以上两个步骤 ,可以建出右图。
因为每个点只属于一条路径,我们可以想到二分图匹配。
因为DAG中每个点的后继结点 就是 它在二分图上的匹配点
所以二分图最大匹配数=DAG中有后继结点的点的个数。
我们要求 结尾结点(没有后继结点的点)的个数, 那么
最小路径数=结尾结点数=n-非结尾结点数=n-最大匹配数。 所以,
最小路径覆盖数=结点数-最大匹配数
看来匈牙利算法模板还挺稳当,居然1A了,耶~~ヾ(=^▽^=)ノ
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=502;
struct Point {
int x,y;
};
struct Client {
int tim;
Point s,t;
char ss[10];
inline void read() {
scanf("%s%d%d%d%d",ss,&s.x,&s.y,&t.x,&t.y);
tim=((ss[0]-'0')*10+ss[1]-'0')*60+(ss[3]-'0')*10+ss[4]-'0';
}
}p[MAXN];
int n,link[MAXN];
bool cov[MAXN],mp[MAXN][MAXN];
inline int abs(int a) {return a>0?a:-a;}
inline int dis(Point a,Point b) {
return abs(a.x-b.x)+abs(a.y-b.y);
}
inline bool check(Client a,Client b) {
int t1=a.tim+dis(a.s,a.t)+dis(a.t,b.s);
int t2=b.tim;
return t1<t2;
}
bool dfs(int p) {
for (int i=1;i<=n;++i) {
if (mp[p][i]&&!cov[i]) {
cov[i]=true;
if (link[i]==-1||dfs(link[i])) {
link[i]=p;
return true;
}
}
}
return false;
}
int main() {
// freopen("UVAlive 3126.in","r",stdin);
int kase;
scanf("%d",&kase);
while (kase--) {
memset(mp,false,sizeof(mp));
memset(link,-1,sizeof(link));
scanf("%d",&n);
for (int i=1;i<=n;++i) p[i].read();
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
mp[i][j]=check(p[i],p[j]);
int mat=0;
for (int i=1;i<=n;++i) {
memset(cov,false,sizeof(cov));
if (dfs(i)) ++mat;
}
printf("%d\n",n-mat);
}
return 0;
}