Delivery Delays
题意
100010001000个点,500050005000条边的无向图,披萨店在111号店.100010001000份披萨订单,每个订单有下单时间,送达地点,披萨制作出来的时间.你是快递员初始在111号点,每次可以拿无穷多披萨,送完以后返回111号点继续送,送餐的时候要求按照下单顺序送达,求等待时间最长的顾客的最小等待时间.
题解
其实这道题不难,读题的时候读漏了一个条件…然后就GG了.
最小化最大值的问题,我们可以思考二分答案再check的套路进行.
二分等待时间最长的顾客的等待时间MMM,则其他所有顾客的等待时间不应超过MMM.
如何checkcheckcheck呢?
答:我们可以用dpdpdp的方法进行checkcheckcheck.
考虑到所有的披萨都必须按照下单时间顺序送达,那么可以想象到最优的方案应该会将披萨序列分成若干小段,每一段都是从111号点出发,拿上该段所有的披萨,然后以最短路的形式,依次将披萨送达,最后回道111点.
那么我们就可以定义dp[i]dp[i]dp[i]表示将前iii块披萨准时送达,且最后回到111点出所花费的最小时间.
转移方程是O(n)O(n)O(n)的
对于iii这个点,将所有j≥i+1j \ge i+1j≥i+1的dp[j]dp[j]dp[j]全部更新.
定义len[i][j]len[i][j]len[i][j]表示从111出发,依次经过iii,i+1i+1i+1,…,jjj这些点的最短路径长度.
当用dp[i]dp[i]dp[i]来更新dp[j]dp[j]dp[j]的时候
-
我们注意到出发时间一定不能小于max{dp[i],t[i+1],t[i+2],...,t[j]}max\{dp[i],t[i+1],t[i+2],...,t[j]\}max{dp[i],t[i+1],t[i+2],...,t[j]},因为必须等这些披萨都制作完成后才能触出发
-
并且出发时间也一定不能大于min{M+s[i+1]−len[i][i+1],...,M+s[j]−len[i][j]}min\{M+s[i+1]-len[i][i+1],...,M+s[j]-len[i][j]\}min{M+s[i+1]−len[i][i+1],...,M+s[j]−len[i][j]}.
因为对于每个ttt,满足i+1≤t≤ji+1 \le t \le ji+1≤t≤j,必然有len[i][t]+st−s[j]≤Mlen[i][t]+st -s[j] \le Mlen[i][t]+st−s[j]≤M,也即st≤M−len[i][t]+s[j]st \le M - len[i][t] +s[j]st≤M−len[i][t]+s[j]
同时满足这两个条件的jjj才能由iii进行转移.
如果最后dp[k]dp[k]dp[k]被更新过,那么限制MMM就是可星的,否则布星.
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
const int N = 1007;
typedef long long LL;
const LL inf = 1e15;
typedef std::pair<LL,int> pii;
std::vector<pii> edge[N];
LL dis[N][N],dp[N];
void Dij(LL D[N],int s) {rep(i,0,N-1) D[i] = inf;std::priority_queue<pii,std::vector<pii>,std::greater<pii> > Q;D[s] = 0;Q.push((pii){D[s],s});while(!Q.empty()) {pii p = Q.top();Q.pop();int u = p.second;if(D[u] < p.first) continue;for(pii e : edge[u]) {int v = e.second;LL c = e.first;if(D[v] > D[u] + c) {D[v] = D[u] + c;Q.push((pii){D[v],v});}}}
}
LL s[N],t[N],u[N];
int n,m,k;
bool check(LL M) {dp[0] = 0;rep(i,1,k) dp[i] = inf;rep(i,0,k-1) {LL st = dp[i],len = 0,mxst = inf;rep(j,i+1,k) {if(j == i+1) len += dis[1][u[i+1]];else len += dis[u[j-1]][u[j]];st = std::max(st,t[j]);//离开1号点的时间mxst = std::min(mxst,M-len+s[j]);LL wait = st+len-s[j];//当前点等待时间if(wait <= M && st <= mxst) dp[j] = std::min(dp[j],st+len+dis[u[j]][1]);else break;}}return dp[k] < inf;
}
int main() {std::ios::sync_with_stdio(false);std::cin >> n >> m;rep(i,1,m) {int a,b;LL c;std::cin >> a >> b >> c;edge[a].push_back((pii){c,b});edge[b].push_back((pii){c,a});}rep(i,1,n) {Dij(dis[i],i);}std::cin >> k;rep(i,1,k) {std::cin >> s[i] >> u[i] >> t[i];}LL l = 0,r = inf;while(r > l) {LL mid = (l + r) >> 1;if(check(mid)) r = mid;else l = mid + 1;}std::cout << l << std::endl;
}