题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1127
题目大意:现在有n个客户,他们要乘出租车,他们每个人,有一个出发时间t,起点位置和终点位置。现在要安排尽量少的出租车送他们到目的地。出租车必须在客户出发前至少提前一分钟赶到那个客户出发的位置才行,或者这个客户就是这辆出租车的第一个乘客。让你输出需要的最少的出租车数。
解题思路:DAG的最小路径覆盖。先开始我们建边,如果客户u和客户v能用一辆出租车搞定,那么就连一条边,由于时间是天然顺序,所以这个连出来的图,不会有环,即是一个 DAG。那么我们的问题就变成了,找出最少的路径个数,使得每个点都恰好在一条路径上。这就是DAG的最小路径覆盖问题。
算法过程是:我们把一个点对等的拆成两个点,一个放左边,一个放右边。然后像之前那样左右连边。然后找最大匹配,匹配数等于我们不需要另找出租车的客户数。为什么?因为想想怎么做的最大匹配,找增广路,对,那么这就相当于再找一条简单路径。每次找到一条增广路,就相当于多了一个节点在同一路径上,也就是不要再另外开路径了。也就是我们只要就解决最多能找到多少条增广路,也就是最大匹配了。所以答案就是 n-最大匹配数。
题目大意:现在有n个客户,他们要乘出租车,他们每个人,有一个出发时间t,起点位置和终点位置。现在要安排尽量少的出租车送他们到目的地。出租车必须在客户出发前至少提前一分钟赶到那个客户出发的位置才行,或者这个客户就是这辆出租车的第一个乘客。让你输出需要的最少的出租车数。
解题思路:DAG的最小路径覆盖。先开始我们建边,如果客户u和客户v能用一辆出租车搞定,那么就连一条边,由于时间是天然顺序,所以这个连出来的图,不会有环,即是一个 DAG。那么我们的问题就变成了,找出最少的路径个数,使得每个点都恰好在一条路径上。这就是DAG的最小路径覆盖问题。
算法过程是:我们把一个点对等的拆成两个点,一个放左边,一个放右边。然后像之前那样左右连边。然后找最大匹配,匹配数等于我们不需要另找出租车的客户数。为什么?因为想想怎么做的最大匹配,找增广路,对,那么这就相当于再找一条简单路径。每次找到一条增广路,就相当于多了一个节点在同一路径上,也就是不要再另外开路径了。也就是我们只要就解决最多能找到多少条增广路,也就是最大匹配了。所以答案就是 n-最大匹配数。
综上:
DAG的最小路径覆盖:一个DAG图,在图中找出尽量少的路径,使每个节点严格属于一条路径。
DAG的最小路径覆盖 = n-最大匹配数。这个算法也同样适用于带权的DAG,而不适用于非DAG的图。
另外,本人有个疑问,怎么样才能比较好的输出这个最小路径覆盖的解集(也就是具体方案)呢?自己想的方法很烦,不行,望各位大牛指点。。
代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 555;
struct Point
{
int x,y;
};
struct Person
{
int time;
Point s,t;
void read()
{
char str[11];
scanf("%s%d%d%d%d",str,&s.x,&s.y,&t.x,&t.y);
time = ((str[0]-'0')*10+str[1]-'0')*60+(str[3]-'0')*10+str[4]-'0';
}
} person[MAXN];
int dis(Point& a,Point& b)
{
return abs(a.x-b.x)+abs(a.y-b.y);
}
int check(int a,int b)
{
int t1 = person[a].time+dis(person[a].s,person[a].t)+dis(person[a].t,person[b].s);
int t2 = person[b].time;
if(t1+1 <= t2)
return 1;
else return 0;
}
int w[MAXN][MAXN];
int s[MAXN],t[MAXN],left[MAXN];
int match(int i,int n)
{
s[i] = 1;
for(int j = 1;j <= n;j++)
if(!t[j] && w[i][j])
{
t[j] = 1;
if(!left[j] || match(left[j],n))
{
left[j] = i;
return 1;
}
}
return 0;
}
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++)
person[i].read();
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
{
if(check(i,j)) w[i][j] = 1;
else w[i][j] = 0;
//printf(" i = %d,j = %d,w = %d\n",i,j,w[i][j]);
}
memset(left,0,sizeof(left));
int ans = 0;
for(int i = 1;i <= n;i++)
{
memset(s,0,sizeof(s));
memset(t,0,sizeof(t));
if(match(i,n))
ans++;
}
ans = n-ans;
printf("%d\n",ans);
}
return 0;
}