UVA1201 Taxi Cab Scheme(二分图最小点覆盖)
题意
你在一座城市里负责一个大型活动的接待工作。明天将有 \(m\)位客人从城市的不同位置出发,到达他们各自的目的地。已知每人的出发时间、出发地点和目的地,你的任务是用尽量少的出租车送他们,使得每次出租车接客人时,至少能提前一分钟到达他所在的位置。注意,为了满足这一条件,要么这位客人是这辆出租车接送的第一个人,要么在接送完上一个客人后,有足够的时间从上一个目的地开到这里。
为简单期间,假定城区是网格型的,地址用坐标 \((x,y)\) 表示。出租车从 \((x_1,y_1)\) 处到 \((x_2,y_2)\) 处需要行驶 \(|x_1-x_2|+|y_1-y_2|\) 分钟。
数据范围
\(m\leq 500\) 。
思路
如果一位司机接完客人 \(i\) 并从终点赶到客人 \(j\) 的起点时客人 \(j\) 还没到,那么显然这位司机就能够接待这两位客人。可以考虑从客人 \(i\) 向客人 \(j\) 连一条边,由于此时必然满足 \(i\) 的出发时间在 \(j\) 前面。将所有满足上述条件的客人都连起来,最终得到的必然是一张有向无环图。
根据二分图的判定定理,当且仅当图中不存在长度为奇数的环(奇环)时,原图就是一个二分图。那么上面建成的无环图就必然是一张二分图。
将所有的客人都分别建在二分图中的两侧,左侧代表司机第一个接待的客人,右边代表后续接待的客人(这里并不需要考虑哪些客人放左边)。此时只需要关注从左侧连向右侧的边,不难发现,题意要求的是用最少的司机接待所有客人。这符合二分图最小覆盖的定义,即给定一张二分图,求出一个最小的点集 \(S\),使得图中任意一条边都有至少一个端点属于 \(S\)。
根据 König 定理,二分图最小点覆盖包含的点数等于二分图最大匹配包含的边数。只需对已经建出的有向无环图求一遍二分图最大匹配即可。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,M=N*N;
struct edge{int v,nex;}e[M];
int tim,n,vis[N],match[N],h[N],idx; void add(int u,int v){e[++idx].v=v;e[idx].nex=h[u];h[u]=idx;}
struct cus{int t,sx,sy,tx,ty;bool operator <(const cus &b)const{return t<b.t;}}p[N];
int dis(cus a,cus b){return abs(a.tx-b.sx)+abs(a.ty-b.sy);}
bool find(int u)
{
for(int i=h[u];i;i=e[i].nex)
{
int v=e[i].v;if(vis[v]==tim) continue;vis[v]=tim;
if(!match[v]||find(match[v])) {match[v]=u;return true;}
}
return false;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);for(int hou,min,i=1;i<=n;i++) scanf("%d:%d%d%d%d%d",&hou,&min,&p[i].sx,&p[i].sy,&p[i].tx,&p[i].ty),p[i].t=hou*60+min;
sort(p+1,p+n+1);int ans=n;memset(h,0,sizeof(h));idx=tim=0;memset(match,0,sizeof(match));
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(p[j].t>p[i].t+dis(p[i],p[i])+dis(p[i],p[j])) add(i,j);
for(int i=1;i<=n;i++)
{
tim++;if(find(i)) ans--;
}
printf("%d\n",ans);
}
return 0;
}