题意:你经营着一家披萨店,该披萨店在1号点,每天都会有很多的订单,第i个订单是在si时刻发来的,收货地点在ui,该披萨在ti时刻才可以做好。问你每个订单的收货时间-发出时间(即买家等待时间)的最大值 的最小值 是多少。
即用dp[i]表示第i个订单,让你最小化 Max{dp[i]-s[i]}。
数据输入:
第一行:n(点的数目,n<=1000),m(边的数目,m<=5000)
接下来m行: x,y,z (表示从x到y直接有一条双向边,长度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;
}