UVALive 3126 Taxi Cab Scheme(DAG的最小路径覆盖)

题目: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-最大匹配数。

    综上:

    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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值