【JSOI2016】飞机调度

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。

分析

//比赛时思路想出来了,但是最小路径覆盖这一块脑抽了,没打匈牙利。。。

如果每个机场每个时间都建一个点,那么复杂度很大。
但是我们发现,按照上面的方法建出来的点,有很多都是没用的。
如果我们对于M个航班来建点呢?

每个航班可以拆成两个点,然后对于一个航班的终点,枚举另一个航班的起点,如果能在规定时间内到达就连一条边。这个可以给原图做一遍弗洛伊德得出(注意有n^2条边,跑Dij或spfa就是作死)。还有就是用if语句代替min,min()常数大。

这样就可以把问题转换为最小路径覆盖,用匈牙利算法解决!

注意细节

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long LL;

int n,m,cnt,tot,I,X[505],Y[505],D[505],dis[505][505],d[505][505],v[505],ans,f[505],visit[505];

bool g[505][505];

bool check(int x)
{
    if (visit[x]==I) return 0;
    visit[x]=I;
    for (int i=1;i<=m;i++) if (g[x][i] && (!f[i] || check(f[i])))
    {
        f[i]=x; return 1;
    }
    return 0;
}

int main()
{
    freopen("flight.in","r",stdin); freopen("flight.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&v[i]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            scanf("%d",&d[i][j]);
            if (i!=j) dis[i][j]=d[i][j]+v[j];
        }
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++) if (i!=k)
            for (int j=1;j<=n;j++)
                if (dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];
    for (int i=1;i<=m;i++) scanf("%d%d%d",&X[i],&Y[i],&D[i]);
    for (int i=1;i<=m;i++)
        for (int j=1;j<=m;j++) if (i!=j && dis[Y[i]][X[j]]+D[i]+d[X[i]][Y[i]]+v[Y[i]]<=D[j])
            g[i][j]=1;
    for (I=1;I<=m;I++) ans+=check(I);
    printf("%d\n",m-ans);
    fclose(stdin); fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值