1000 1000 1000个点, 5000 5000 5000条边的无向图,披萨店在 1 1 1号店. 1000 1000 1000份披萨订单,每个订单有下单时间,送达地点,披萨制作出来的时间.你是快递员初始在 1 1 1号点,每次可以拿无穷多披萨,送完以后返回 1 1 1号点继续送,送餐的时候要求按照下单顺序送达,求等待时间最长的顾客的最小等待时间.
其实这道题不难,读题的时候读漏了一个条件…然后就GG了.
最小化最大值的问题,我们可以思考二分答案再check的套路进行.
二分等待时间最长的顾客的等待时间 M M M,则其他所有顾客的等待时间不应超过 M M M.
如何 c h e c k check check呢?
答:我们可以用 d p dp dp的方法进行 c h e c k check check.
考虑到所有的披萨都必须按照下单时间顺序送达,那么可以想象到最优的方案应该会将披萨序列分成若干小段,每一段都是从 1 1 1号点出发,拿上该段所有的披萨,然后以最短路的形式,依次将披萨送达,最后回道 1 1 1点.
那么我们就可以定义 d p [ i ] dp[i] dp[i]表示将前 i i i块披萨准时送达,且最后回到 1 1 1点出所花费的最小时间.
转移方程是 O ( n ) O(n) O(n)的
对于 i i i这个点,将所有 j ≥ i + 1 j \ge i+1 j≥i+1的 d p [ j ] dp[j] dp[j]全部更新.
定义 l e n [ i ] [ j ] len[i][j] len[i][j]表示从 1 1 1出发,依次经过 i i i, i + 1 i+1 i+1,…, j j j这些点的最短路径长度.
当用 d p [ i ] dp[i] dp[i]来更新 d p [ j ] dp[j] dp[j]的时候
我们注意到出发时间一定不能小于 m a x { d p [ 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]},因为必须等这些披萨都制作完成后才能触出发
并且出发时间也一定不能大于 m i n { M + s [ i + 1 ] − l e n [ i ] [ i + 1 ] , . . . , M + s [ j ] − l e n [ 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]}.
因为对于每个 t t t,满足 i + 1 ≤ t ≤ j i+1 \le t \le j i+1≤t≤j,必然有 l e n [ i ] [ t ] + s t − s [ j ] ≤ M len[i][t]+st -s[j] \le M len[i][t]+st−s[j]≤M,也即 s t ≤ M − l e n [ i ] [ t ] + s [ j ] st \le M - len[i][t] +s[j] st≤M−len[i][t]+s[j]
同时满足这两个条件的 j j j才能由 i i i进行转移.
如果最后 d p [ k ] dp[k] dp[k]被更新过,那么限制 M M M就是可星的,否则布星.
#include
#include
#include
#include
#include
#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;
}