JZOJ 4465【GDOI2016模拟4.22】飞机调度

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个航班。

Analysis

这道题图的模型很明显。
首先把图构出来。
发现不同航线衔接之间要跑最短路。
(原来n=500跑floyd如果打得优美不会TLE)
然后把能衔接的航线之间在二分图上连边。然后发现问题实质上是做最小路径覆盖。
所以直接做就好了。
最小路径覆盖详情参见网络流与线性规划

Code

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=1010;
struct line
{
    int x,y,d;
}c[N];
int n,m,st,dis[N][N],map[N][N],b[N],a[N][N],pos[N],bz[N];
bool match(int v)
{
    if(bz[v]==st) return 0;
    bz[v]=st;
    fo(i,1,a[v][0])
    {
        int u=a[v][i];
        if(!pos[u] || match(pos[u]))
        {
            pos[u]=v;
            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",&b[i]);
    fo(i,1,n)
        fo(j,1,n)
        {
            scanf("%d",&dis[i][j]);
            if(i!=j) dis[i][j]+=b[j];
            map[i][j]=dis[i][j];
        }
    fo(k,1,n)
        fo(i,1,n)
            if(i!=k)
                fo(j,1,n)
                    if(k!=j && j!=i && map[i][k]+map[k][j]<map[i][j]) map[i][j]=map[i][k]+map[k][j];
    fo(i,1,m) scanf("%d %d %d",&c[i].x,&c[i].y,&c[i].d);
    fo(i,1,m)
        fo(j,1,m)
            if(i!=j && c[i].d+dis[c[i].x][c[i].y]+map[c[i].y][c[j].x]<=c[j].d)
            {
                a[i][++a[i][0]]=m+j;
                a[m+j][++a[m+j][0]]=i;
            }
    int ans=0;
    for(st=1;st<=m;st++)
        if(match(st)) ans++;
    printf("%d",m-ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值