NCPC2018 D.Delivery Delays[二分答案+DP check]

Delivery Delays

题意

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 ji+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]的时候

  1. 我们注意到出发时间一定不能小于 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]},因为必须等这些披萨都制作完成后才能触出发

  2. 并且出发时间也一定不能大于 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+1tj,必然有 l e n [ i ] [ t ] + s t − s [ j ] ≤ M len[i][t]+st -s[j] \le M len[i][t]+sts[j]M,也即 s t ≤ M − l e n [ i ] [ t ] + s [ j ] st \le M - len[i][t] +s[j] stMlen[i][t]+s[j]

同时满足这两个条件的 j j j才能由 i i i进行转移.

如果最后 d p [ k ] dp[k] dp[k]被更新过,那么限制 M M M就是可星的,否则布星.

代码

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值