LA 3126 - Taxi Cab Scheme DAG上的二分匹配

题目链接LA 3126 - Taxi Cab Scheme

题目大意:n个客人,从城市的不同位置出发,到达他们的目的地。已知每个人的出发时间hh:mm,出发地点(x1,y1)及目的地(x2,y2),要求使用最少的出租车接送乘客,使得每个顾客的要求都被执行,且每次出租车接客时需要至少提前一分钟到达乘客所在的位置。城区是网格型的,地址用(x,y)表示,出租车从(x1,y1)到(x2,y2)需要行驶|x1 - x2| + |y1 - y2|分钟。

题目分析:本题的模型是DAG上的最小路径覆盖。将每个客人视为一个节点,如果接送完顾客i后还可以继续接送顾客j,则对应DAG中的一条边i -> j。对每个节点拆点为i,i',如果图中存在有向边i -> j,则建边(i,j')。设二分图的最大匹配数为m,则结果即为n - m。

代码如下:


#include <stdio.h>  
#include <string.h>  
#include <algorithm>
#define abs(X) ((X) > 0 ? (X) : -(X))
#define REP(I, X) for(int I = 0; I < X; ++I)
#define clear(A, X, SIZE) memset(A, X, sizeof(A[0]) * (SIZE + 1))  
using namespace std;  
const int maxN = 2000;  
const int maxE = 1000000;  
struct Edge{  
        int v, n;  
}edge[maxE];  
struct Node{
        int s, e, x1, y1, x2, y2;
}a[maxN];
int adj[maxN], cntE, vis[maxN], link[maxN];  
int n;
void addedge(int u, int v){  
        edge[cntE].v = v; edge[cntE].n = adj[u]; adj[u] = cntE++;  
}  
int find(int u){  
        for(int i = adj[u]; ~i; i = edge[i].n) if(!vis[edge[i].v]){  
                int v = edge[i].v;  
                vis[v] = 1;  
                if(link[v] == -1 || find(link[v])){  
                        link[v] = u;  
                        return 1;
                }  
        }  
        return 0;  
}  
int match(){  
        int ans = 0;  
        clear(link, -1, n);  
        REP(i, n){  
                clear(vis, 0, n);  
                ans += find(i);  
        }  
        return ans;  
}  
void work(){  
        int h, m;  
        scanf("%d", &n);
        clear(adj, -1, n);  
        cntE = 0;  
        REP(i, n){  
                scanf("%d:%d%d%d%d%d", &h, &m, &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
                a[i].s = h * 60 + m;
                a[i].e = a[i].s + abs(a[i].x1 - a[i].x2) + abs(a[i].y1 - a[i].y2);
        }
        REP(i, n) REP(j, n){	
                if(a[i].e + abs(a[i].x2 - a[j].x1) + abs(a[i].y2 - a[j].y1) < a[j].s) addedge(i, j);
        }
        printf("%d\n", n - match());  
}  
int main(){ 
        int t; 
        for(scanf("%d", &t); t; --t) work();
        return 0;  
}  



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值