NCPC2018 Delivery Delays

1 篇文章 0 订阅

 

题意:你经营着一家披萨店,该披萨店在1号点,每天都会有很多的订单,第i个订单是在si时刻发来的,收货地点在ui,该披萨在ti时刻才可以做好。问你每个订单的收货时间-发出时间(即买家等待时间)的最大值 的最小值 是多少。

即用dp[i]表示第i个订单,让你最小化 Max{dp[i]-s[i]}

数据输入:

第一行:n(点的数目,n<=1000),m(边的数目,m<=5000)

接下来m行: x,y,z (表示从xy直接有一条双向边,长度0=<z<=1e8)

接下来一行:k(订单数目,k<=1000)

接下来k行:si,ui,ti(描述意义如上,0<=ti<=si<=1e8

Solution

 

要最小化 最大值,一般通过二分来确定,我们在这里二分这个等待时间

Part1 预处理出来每个点到其余点的最小距离,n遍dij即可

Part 2 判断这个等待时间是否可行

  考虑用dp来算出来到达第i号订单的点的最快时间

  因为可以屯着送披萨,我们就可以把这个送订单分成若干份若干份的送,可以考虑把  i号订单起连续到j号订单一起送,然后到      达第j号订单的最短时间,j+1号订单的出发  时间必须>=送达第j号订单的时间+从j号订单的点回到1号点的时间。

  即第i号订单的出发时间为max(dp[i-1]+dis[1][u[i-1]],t[i]);

  那么就开始dp,首先记第i号订单的出发时间为statime,那么,送到i号订单时间  就  是statime+dis[1][u[i]],如果想把第i+1个订    单和第i个一起送,那么,出发时间就得变  成max(statime,t[i+1]),但到达第i个点的时间不会再提前了,就不用更新dp[i]了,此    时判断把i+1和i一起送会不会超出等待时间,如果会,就不再连着送了,如果不会,  就可以得到到达i+1号订单的时间了,更新    这个dp[i+1],以及连着送过程重的最大等  待时间。

  因为从不同的点连着送到达i会得到不同的dp[i],所以要以每个点为起点跑一遍那个  dp过程

  时间复杂度O(k²)

  Part 3 二分

 这个应该不用说了,但是这道题的数据比较毒瘤,首先观察每条边的权值,发现最小值会是0,再观察订单的si和ti,会发现si可   能等于ti,即不需要等待就可以拿到pizza,被瞬间转移限制想象力,观察数据可得二分下限为0,上限为1e15

总时间复杂度O( n*n*log(e)  k*k*log(1e15) )

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
struct node{
    long long u,to,w;
}edge[10010];
struct di{
    long long u, w;
    di(long long _u=0, long long _w=0):u(_u),w(_w){}
    bool friend operator < (di da, di db){
        return da.w<db.w;
    }
    bool friend operator > (di da, di db){
        return da.w>db.w;
    }
}ex;
long long head[maxn], cnt;
long long dis[maxn][maxn], s[maxn], u[maxn], t[maxn], dp[maxn], mista[maxn];
int n,m,k;
inline void add(long long x, long long y, long long w){
    cnt++;  edge[cnt].u=y;  edge[cnt].w=w;  edge[cnt].to=head[x];   head[x]=cnt;
    return ;
}
inline void get_dis(long long uu){
    priority_queue< di, vector<di>, greater<di> >q;
    q.push(di(uu,0));
    cnt=0;
    long long tu, tw;
    while(!q.empty()){
        ex=q.top(); q.pop();
        if (dis[uu][ex.u]<ex.w){
            continue;
        }
        dis[uu][ex.u]=ex.w;
        cnt++;
        if (cnt==n){
            break;
        }
        for (long long i=head[ex.u];i>0;i=edge[i].to){
            tu=edge[i].u;   tw=ex.w+edge[i].w;
            if (tw<dis[uu][tu]){
                dis[uu][tu]=tw;  q.push(di(tu,tw));
            }
        }
    }
    return ;
}
void pre_init(){//dij get dis
    for (int i=1;i<=n;++i){
        for (int j=1;j<=n;++j){
            dis[i][j]=1e18;
        }
    }
    for (long long i=1;i<=n;++i){
        get_dis(i);
    }
    return ;
}
void read_init(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i){
        head[i]=-1;
    }
    cnt=0;
    long long x,y,w;
    while(m--){
        scanf("%lld%lld%lld",&x,&y,&w);
        add(x,y,w); add(y,x,w);
    }
    scanf("%d",&k);
    for (int i=1;i<=k;++i){
        scanf("%lld%lld%lld",&s[i],&u[i],&t[i]);
    }
    pre_init();
    return ;
}
inline bool pj(long long x){
    for (int i=1;i<=k;++i){
        dp[i]=1e18;
    }
    long long wtime, exdis, statime;
    for (int i=1;i<=k;++i){
        statime = max (dp[i-1] + dis[1][u[i-1]] , t[i] );   //出发时间
        exdis = statime + dis[1][u[i]];                     //到达时间
        wtime = exdis - s[i];                               //等待时间
        if (wtime > x){
            continue;
        }
        dp[i]=min(dp[i],exdis);
        for (int j=i+1;j<=k;++j){
            if (statime>=t[j]){
                exdis = exdis + dis[u[j-1]][u[j]];
                wtime = max(wtime , exdis - s[j] );
            }else{
                exdis = exdis + dis[u[j-1]][u[j]] + t[j] - statime;
                wtime = max(wtime + t[j]-statime , exdis - s[j] );
            }
            statime=max(statime,t[j]);
            if (wtime>x){
                break;
            }
            dp[j]=min(exdis,dp[j]);
        }
    }
    if (dp[k]==1e18){
        return false;
    }
    return true;
}
void sol(){
    long long l,r,mid;
    l=0;    r=1e18;
    if (pj(0)){
        puts("0");
        return ;
    }
    while(l<r){
        mid=(l+r)>>1;
        if (pj(mid)){
            r=mid;
            if (l+1==r){
                break;
            }
        }else{
            l=mid;
            if (l+1==r){
                break;
            }
        }
    }
    if (pj(l)){
        r=l;
    }
    printf("%lld\n",r);
    return ;
}
int main(){
    read_init();
    dp[0]=0;   t[0]=0;     u[0]=1;
    sol();
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值