JZOJ.4465[GDOI2016模拟4.22] 飞机调度 解题报告

飞机调度

题目描述

作为一个旅行达人以及航空公司的金卡会员,你每一年的飞行里程可以绕赤道几周了。你发现,航空公司为了提高飞机的使用率,并不是简单的一条航线使用一架飞机来回飞,而是会让同一架飞机连续不停地飞不同的航线,甚至有的时候为了能够完成飞机的调度,航空公司还会增开一些临时航线——在飞机转场的同时顺路捎一些乘客。你研究了一下GDOI著名航空公司GD Airways的常规直飞航线,你想知道,在最佳调度方案下,GD Airways最少需要多少架飞机才能成功执飞这所有的航线。
GDOI王国里有N个机场,编号为1到N。从i号机场到j号机场需要飞行Ti,j的时间。由于风向,地理位置和航空管制的因素,Ti,j和Tj,i并不一定相同。
此外,由于飞机降落之后需要例行维修和加油。当一架飞机降落k号机场时,需要花费Pk的维护时间才能再次起飞。
GD Airways一共运营M条航线,其中第i条直飞航线需要在Di时刻从Xi机场起飞,不经停,飞往Yi机场。
为了简化问题,我们假设GD Airways可以在0时刻在任意机场任意多架加油维护完毕的飞机;为了减少飞机的使用数,我们允许GD Airways增开任意多条临时航线以满足飞机的调度需要。
你想知道,理论上GD Airways最少需要多少架飞机才能完成所有这M个航班。

输入格式

从文件flight.in中读入数据。
输入一行包含两个正整数N和M。
接下来一行包含N个正整数表示每一个机场的飞机维护时间。
接下来N行,每行N个非负整数,其中第i行第j个非负整数为T[i][j],表示从第i号机场飞往j号机场所需要花费的时间。数据保证T[i][i]=0。
接下来M行,每行3个正整数,其中第i行为Xi,Yi,Di,表示第i条航线的起飞机场,降落机场,以及起飞时间。数据保证Xi≠Yi。

输出格式

输出到文件flight.out中。
输出文件包含一行一个正整数,表示GD Airways理论上最少需要的飞机数。

样例输入

样例输入1:

3 3
100 1 1
0 1 1
1 0 5
2 1 0
1 2 1
2 1 1
3 1 9

样例输入2:

3 3
100 1 1
0 1 1
1 0 5
2 1 0
1 2 1
2 1 1
3 1 8

样例输出

样例输出1:

2

样例输出2:

3

数据范围

对于30%的数据满足N,M<=10;
对于60%的数据满足N,M<=100;
对于100%的数据满足1<=N,M<=500,0<=Pi,Ti,j<= 106 ,1<=Di<= 106

样例解释

在第一个样例中,GD Airways可以在0时刻在2号机场安排一架飞机并执飞第2条航线(2–>1)。此外还需要在0时刻在1号机场安排一架飞机,这架飞机首先执飞第1条航线(1–>2),然后通过临时新增一条航线从2号机场起飞飞往3号机场,降落3号机场之后执飞第3条航线(3–>1)。
在第二个样例中,执行完第1条航线的飞机无法赶上第3条航线的起飞时间,因此GD Airways必须使用3架不同的飞机才能完成所有的航班。

题解

直接讲100分的。
首先,用Floyd求出两两机场之间的到达对方的最短时间,注意不要忘了算上维修时间。
对两趟航班 i j,飞完了 i 航班后飞机可以马上赶到另一个机场飞j航班,则我们让 i j连一条有向边。(此图内一定不会有环,可证)
那我们现在的问题就转化成了用最少的飞机把我们构造出来的图里的全部的点都遍历一次。
我们如何选择一种最优方案呢?
我们发现,每个点都会被遍历一次,对于每个点的子问题就是应该接下来走哪一个点最优,即匹配哪个点可以最优。
此时,这就变成了一个点的匹配问题。
u 拆成两个点U U 。若 u 有一条连向v的边,那我们把 U V连向一条边。
这时,我们的问题再次转化为上述二分图的最大匹配。
答案即为航班数 m <script type="math/tex" id="MathJax-Element-16">m</script>减去最大匹配数。

二分图的匹配一般都用匈牙利算法。

大家可以自己查一下或者点一下链接:
http://blog.csdn.net/dark_scope/article/details/8880547

Code(Pascal)

var
    cqy,x,y,d,cost:array[0..1000] of longint;
    bj:array[0..1002,0..1002] of boolean;
    t,zd:array[0..502,0..502] of longint;
    bz:array[0..1002] of boolean;
    n,j,m,k,l,i,o,p,www:longint;
    ans,fly:int64;
function max(a,b:int64):int64;
    begin
        if a>b then exit(a)
        else exit(b);
    end;
function min(a,b:int64):int64;
    begin
        if a<b then exit(a)
        else exit(b);
    end;
function dg(o:longint):boolean;
    var
        i:longint;
    begin
        if o=0 then exit(true);
        for i:=m+1 to 2*m do
        if bj[o,i] and not(bz[i]) then
        begin
            bz[i]:=true;
            if (cqy[i]=0) or dg(cqy[i]) then
            begin
                cqy[i]:=o;
                exit(true);
            end;
        end;
        exit(false);
    end;
begin
    assign(input,'flight.in'); reset(input);
    assign(output,'flight.out'); rewrite(output);
    readln(n,m);
    for i:=1 to n do
    read(cost[i]);
    readln;
    for i:=1 to n do
    begin
        for l:=1 to n do
        read(t[i,l]);
        readln;
    end;
    for i:=1 to n do
    for l:=1 to n do
    zd[i,l]:=t[i,l]+cost[l];
    for j:=1 to n do
    for i:=1 to n do
    for l:=1 to n do
    zd[i,l]:=min(zd[i,l],zd[i,j]+zd[j,l]);
    for i:=1 to n do
    zd[i,i]:=0;
    for i:=1 to m do
    readln(x[i],y[i],d[i]);
    for i:=1 to m do
    begin
        fly:=d[i]+t[x[i],y[i]]+cost[y[i]];
        for l:=1 to m do
        if fly+zd[y[i],x[l]]<=d[l] then
        bj[i,l+m]:=true;
    end;
    ans:=0;
    for i:=1 to m do
    begin
        for l:=m+1 to m*2 do
        bz[l]:=false;
        if dg(i) then inc(ans);
    end;
    writeln(m-ans);
    close(input);
    close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值