LA 3126 二分匹配---DAG中的最小路径应用

题意:有 n 个顾客 , 需要坐出租车从一个地方去另一个地方 , 每个顾客的出发时间、出发地点、目的地点都已给出 , 从出发地点到目的地点的时间为两地之间的路径长度 , 并且出租车要比顾客的出发时间早一分钟到达 , 问最少需要派出多少辆出租车。


解法:我们先这样来构图 , 每个顾客是一个结点,如果同一个出租车在接完客人 u 之后还来得及节客人 v , 那么就在 u 到 v 之间连一条有向边 。 由此可以发现 , 这个图是一个DAG , 那么我们就只需要找最小路径覆盖(最小路径覆盖:是指在图中找尽量少的路径,去覆盖每一个点,并且每个路径之间没有公共点)。


对于最小路径覆盖,我们先进行拆点  , 把图边为一个二分图 , 把所有结点 拆为两个结点 , 如 结点 u 拆为 u1 , u2 , 如果 u 到 v 有一条有向边 , 那么我们就连接 u1 到 v2 的一条边。

这时我们只需要求出这个图的最大匹配 , 结果就是: n - m(最大匹配数)。


为什么?

因为匹配和路径覆盖是一一对应的 , 对于路径覆盖中的每条简单路径 , 除了最后一个“结尾结点”之外,都有唯一的后继与其对应(也就是匹配结点) , 因此匹配数就是非“结尾结点”的个数 , 但匹配数达到最大时 , 也就是“结尾结点”最小 , 也就是路径最小。


代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <cmath>

using namespace std;

#define maxn 520

vector<int>grap[maxn];
int cab[maxn][4] , cab_t[maxn][2];
int n;
int pre[maxn] , cx[maxn] , cy[maxn];

void init()
{
    for(int i = 0; i < n; i++)
        grap[i].clear();
    memset(cx , -1 , sizeof(cx));
    memset(cy , -1 , sizeof(cy));
}

void create_grap()
{
    int i , j;
    int x , y;
    int h , m;
    for(i = 0; i < n; i++)
    {
        y = abs(cab[i][0]-cab[i][2])+abs(cab[i][1]-cab[i][3]);
        for(j = 0; j < n; j++)
        {
            if(i == j)  continue;
            x = y+abs(cab[j][0]-cab[i][2])+abs(cab[j][1]-cab[i][3]);
            m = (x+cab_t[i][1])%60;
            h = cab_t[i][0]+(x+cab_t[i][1])/60;
            if(h < cab_t[j][0] || (h == cab_t[j][0]&&m < cab_t[j][1]))
                grap[i].push_back(j);
        }
    }
}

int findpath(int u)
{
    int i , v;
    for(i = 0; i < grap[u].size(); i++)
    {
        v = grap[u][i];
        if(!pre[v])
        {
            pre[v] = 1;
            if(cy[v] == -1 || findpath(cy[v]))
            {
                cy[v] = u;
                cx[u] = v;
                return 1;
            }
        }
    }
    return 0;
}

int match()
{
    int i , cas = 0;
    for(i = 0; i < n; i++)
        if(cx[i] == -1)
        {
            memset(pre , 0 , sizeof(pre));
            cas += findpath(i);
        }

    return cas;
}

int main()
{
    int t;
    scanf("%d" , &t);
    while(t--)
    {
        scanf("%d" , &n);
        init();

        int i ;
        for(i = 0; i < n; i++)
            scanf("%d:%d %d %d %d %d" , &cab_t[i][0] , &cab_t[i][1] , &cab[i][0] , &cab[i][1] , &cab[i][2] , &cab[i][3]);
        create_grap();

        int cas = match();
        cout<<n-cas<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值