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

Delivery Delays

題意

10001000個點,50005000條邊的無向圖,披薩店在11號店.10001000份披薩訂單,每個訂單有下單時間,送達地點,披薩製作出來的時間.你是快遞員初始在11號點,每次可以拿無窮多披薩,送完以後返回11號點繼續送,送餐的時候要求按照下單順序送達,求等待時間最長的顧客的最小等待時間.

題解

其實這道題不難,讀題的時候讀漏了一個條件…然後就GG了.

最小化最大值的問題,我們可以思考二分答案再check的套路進行.

二分等待時間最長的顧客的等待時間MM,則其他所有顧客的等待時間不應超過MM.

如何checkcheck呢?

答:我們可以用dpdp的方法進行checkcheck.

考慮到所有的披薩都必須按照下單時間順序送達,那麼可以想象到最優的方案應該會將披薩序列分成若干小段,每一段都是從11號點出發,拿上該段所有的披薩,然後以最短路的形式,依次將披薩送達,最後回道11點.

那麼我們就可以定義dp[i]dp[i]表示將前ii塊披薩準時送達,且最後回到11點出所花費的最小時間.

轉移方程是O(n)O(n)

對於ii這個點,將所有ji+1j \ge i+1dp[j]dp[j]全部更新.

定義len[i][j]len[i][j]表示從11出發,依次經過ii,i+1i+1,…,jj這些點的最短路徑長度.

當用dp[i]dp[i]來更新dp[j]dp[j]的時候

  1. 我們注意到出發時間一定不能小於max{dp[i],t[i+1],t[i+2],...,t[j]}max\{dp[i],t[i+1],t[i+2],...,t[j]\},因爲必須等這些披薩都製作完成後才能觸出發

  2. 並且出發時間也一定不能大於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]\}.
    因爲對於每個tt,滿足i+1tji+1 \le t \le j,必然有len[i][t]+sts[j]Mlen[i][t]+st -s[j] \le M,也即stMlen[i][t]+s[j]st \le M - len[i][t] +s[j]

同時滿足這兩個條件的jj才能由ii進行轉移.

如果最後dp[k]dp[k]被更新過,那麼限制MM就是可星的,否則布星.

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章