【GDOI2016模拟4.22】飞机调度

17 篇文章 0 订阅
10 篇文章 0 订阅

Description

作为一个旅行达人以及航空公司的金卡会员,你每一年的飞行里程可以绕赤道几周了。你发现,航空公司为了提高飞机的使用率,并不是简单的一条航线使用一架飞机来回飞,而是会让同一架飞机连续不停地飞不同的航线,甚至有的时候为了能够完成飞机的调度,航空公司还会增开一些临时航线——在飞机转场的同时顺路捎一些乘客。你研究了一下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个航班。

Input

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

Output

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

Sample Input

样例输入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

Sample Output

样例输出1:
2
样例输出2:
3

Data Constraint

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

Hint

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

Solution

这题看很神奇。
首先我想到了DP。仔细想一想,发现根本无法推出状态转移方程。
接着我想到了网络流,然后发现……
由题意可知,如果一个航班飞完了这个航线,要飞下一条航线,必须要在那班航班开始前到大那个机场并加油完。也就是说飞机不能晚点。
那么一架飞机飞完一条航线后到达下一个航线的起点位置不需要直飞,就用一个Floyd求出最短路。然而飞机到达机场需要修理,可以吧修理的时间算在路线长度内,就不需要再判断了。
如果一个航线的终点ti飞到另一个航线的起点sj,并且可以再时间内的话,就把i与j连边。那么要使所需飞机最少,就是要在图中求最小路径覆盖。
最小路径覆盖用二分图匹配就行了。(匈牙利算法)
注意:飞航线必须直飞,而从一个终点飞到另一个起点要最短路。

code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iostream>
#define N 510
#define MAX 21474836
#define fo(i,a,b) for(int i=a;i<=b;i++)
int map[N][N],a[N][N],p[N],h[N],n,m,last[N],next[N*1000],to[N*1000],b[N],tot=0,st,bz[N]; 
struct note 
{
    int s,t,d;
};
note f[N];
using namespace std;
void putin(int x,int y)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;
}
bool dg(int x)
{
    if (bz[x]==st) return 0;
    bz[x]=st;
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if (b[y]==0 || dg(b[y]))
        {
            b[y]=x;return 1;
        }
    }
    return 0;
}
int main()
{
    freopen("flight.in","r",stdin);freopen("flight.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&p[i]);
    fo(i,1,n) 
        fo(j,1,n) 
        {
            scanf("%d",&map[i][j]);
            if (i!=j) a[i][j]=map[i][j]+=p[j];
        }
    fo(i,1,m) scanf("%d%d%d",&f[i].s,&f[i].t,&f[i].d);
    fo(k,1,n)
        fo(i,1,n)
            fo(j,1,n)
            if (i!=j && a[i][j]>a[i][k]+a[k][j])
            {
                a[i][j]=a[i][k]+a[k][j];
            }
    fo(i,1,m)
        fo(j,1,m)
            if (i!=j && f[i].d+map[f[i].s][f[i].t]+a[f[i].t][f[j].s]<=f[j].d)
            {
                putin(i,j);
            }
    int ans=m;
    for(st=1;st<=m;st++) if (dg(st)) ans--;
    printf("%d",ans);
    fclose(stdin);fclose(stdout);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值