飞机调度

Description

作为一个旅行达人以及航空公司的金卡会员,你每一年的飞行里程可以绕赤道几周了。你发现,航空公司为了提高飞机的使用率,并不是简单的一条航线使用一架飞机来回飞,而是会让同一架飞机连续不停地飞不同的航线,甚至有的时候为了能够完成飞机的调度,航空公司还会增开一些临时航线——在飞机转场的同时顺路捎一些乘客。你研究了一下GDOI著名航空公司GD Airways的常规直飞航线,你想知道,在最佳调度方案下,GD Airways最少需要多少架飞机才能成功执飞这所有的航线。

GDOI王国里有N个机场,编号为1到N。从i号机场到j号机场需要飞行 Ti,j 的时间。由于风向,地理位置和航空管制的因素, Ti,j Tj,i 并不一定相同。

此外,由于飞机降落之后需要例行维修和加油。当一架飞机降落 k 号机场时,需要花费P[k]的维护时间才能再次起飞。

GD Airways一共运营 M 条航线,其中第i条直飞航线需要在Di时刻从Xi机场起飞,不经停,飞往 Yi 机场。

为了简化问题,我们假设GD Airways可以在 0 时刻在任意机场任意多架加油维护完毕的飞机;为了减少飞机的使用数,我们允许GD Airways增开任意多条临时航线以满足飞机的调度需要。

你想知道,理论上GD Airways最少需要多少架飞机才能完成所有这M个航班。

Solution

维护时间只和降落的机场有关,所以我们可以直接把它加入飞行时间内

然而我们可以发现,直飞并不一定耗费最短时间,所以我们还需要做一遍最短路(这里推荐大家打Floyd,SPFA跑完全图嘛……,Floyd跑 ON3 实测是可以过的,SPFA反而超时,我就是被坑了

特别提醒:一开始给定你的航线是不可以走最短路的,因为

第i条直飞航线需要在Di时刻从 Xi 机场起飞,不经停

不经停!

这坑倒了无数人,包括我

继续

我们可以把每个航班拆成两个点,分别是出发和到达

点上记录时刻和所在机场。

我们把到达的点和出发的点分别存在两个数组中

对于每一个到达点 i ,时刻为Time[i],所在机场为 P[i] ,把所有的出发的点扫一遍,出发时刻设为 Time[j] ,所在机场为 P[j]

把所有 Time[i]+dis[P[i]][P[j]]Time[j] (i,j) 连一条边

其中 dis[i][j] 表示从 i 号机场到j号机场的时间

那么我们可以得到一个二分图,跑一遍最大匹配即可(建议打匈牙利)
不会匈牙利的戳这里

http://blog.csdn.net/hzj1054689699/article/details/51035647

这是经典的路径覆盖问题

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note
{
    int x,y,z;
};
bool cmp(note x,note y)
{
    return x.y<y.y;
}
int dis[505][505],wh[501],map[505][505],n,m,a[505][505],dt[5005];
bool bz[505];
note l[505],r[505];
bool find(int k)
{
    int i;
    fo(i,1,a[k][0])
    {
        int p=a[k][i];
        if (bz[p]==0)
        {
            bz[p]=1;
            if (dt[p]==0||find(dt[p]))
            {
                dt[p]=k;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    freopen("flight.in","r",stdin);
    freopen("flight.out","w",stdout);
    scanf("%d%d",&n,&m);
    int i,j,k;
    fo(i,1,n)
    {
        scanf("%d",&wh[i]);
    }
    fo(i,1,n)
    {
        fo(j,1,n)
        {
            scanf("%d",&map[i][j]);
            if (i!=j) map[i][j]+=wh[j]; 
            dis[i][j]=map[i][j];
        }   
    }   
    fo(k,1,n)
        fo(i,1,n)
            fo(j,1,n)
                if (dis[i][j]>dis[i][k]+dis[k][j])  dis[i][j]=dis[i][k]+dis[k][j];
    fo(i,1,m)
    {
        scanf("%d%d%d",&r[i].x,&l[i].x,&r[i].y);
        l[i].y=r[i].y+map[r[i].x][l[i].x];
        l[i].z=r[i].z=i;
    }
    sort(l+1,l+m+1,cmp);
    sort(r+1,r+m+1,cmp);
    fo(i,1,m)
    {
        fo(j,1,m)
        {
            if (l[i].z!=r[j].z&&l[i].y+dis[l[i].x][r[j].x]<=r[j].y) 
            {
                a[i][++a[i][0]]=j;
            }
        }
    }
    memset(dt,0,sizeof(dt));
    int ans=m;
    fo(i,1,m)
    {
        memset(bz,0,sizeof(bz));
        if (find(i)) ans--; 
    }
    cout<<ans;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值