题目
这里写链接内容
题解
注意:题目给出是按照时间给出的顺序。
我们考虑第ii个人要上的楼高,排在第ii个人前面的,所有要人上的楼高度的人都可以被合并在与第ii个人一起被传送上去。
所以我们只需要考虑单调递减的序列就可以了,其它的人我们都可以删掉不看,因为它已经被合并了。
例如:
一个hh序列为1 8 2 5 4 3 2可以被缩短为8 5 4 3 2。
其中1被合并给8,2被合并给5。
我们按照上述方法对序列进行精简,精简之后是一个按照所需要达到楼层严格递减的序列,然后我们就可以进行动态规划了。
然后我们考虑动态规划。
定义状态dp[i]dp[i]表示第ii个位置电梯启动,并且前个人全部被传送到对应楼层所需的最小时间。
转移方程:
对于ii,我们遍历
dp[i]=min(max(dp[j]+2∗h[j+1],t[i]+2∗h[j+1]))dp[i]=min(max(dp[j]+2∗h[j+1],t[i]+2∗h[j+1]))
1. 也就是说当dp[j]<=t[i]dp[j]<=t[i]的时候,我们把第j+1…ij+1…i里面的人全都合并到t[i]t[i]的时间点运送上去。(第一部分)
2. 当dp[j]>t[i]dp[j]>t[i]的时候,我们把j+1…ij+1…i里面的人全都合并到dp[j]dp[j]的时间点运送上去。(第二部分)
我们可以用一个小技巧来处理上面两种情况。
- 由于dp[i]dp[i]是非递减的,我们设置一个变量stst表示dp[st−1]dp[st−1]小于等于当前t[i]t[i]的最大stst,这样的话我们可以一开始就把第一种部分处理完。因为h[st]h[st]是满足1≤j<i,且dp[j]≤t[i]1≤j<i,且dp[j]≤t[i]最小的h[j]h[j]。就是初始化dp[i]=t[i]+2∗h[st]dp[i]=t[i]+2∗h[st],这样一来,接下来的dp方程就被简化为dp[i]=min(dp[j]+2∗h[j+1]),dp[j]>t[i]dp[i]=min(dp[j]+2∗h[j+1]),dp[j]>t[i] 这个式子就很好解决了,我们使用一个优先队列来维护dp[j]+2∗h[j+1]dp[j]+2∗h[j+1]。
- 那么怎么处理dp[j]>t[i]dp[j]>t[i]这个小条件呢?考虑到t[i]t[i]是单调递增的,凡是出现过dp[j]≤t[i]dp[j]≤t[i]的点,那么对于的i≤ki≤k中必定有dp[j]≤t[i]≤t[k]dp[j]≤t[i]≤t[k]。因此在优先队列里面遇到dp[j]≤t[i]dp[j]≤t[i]这样的点,我们直接把它删除掉就可以了。
这样的时间复杂度是O(nlog(n))O(nlog(n))
代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int n,tail;
#define pr(x) cout<<#x<<":"<<x<<endl
const int maxn = 2e5+7;
ll ts[maxn],hs[maxn],dp[maxn];
typedef pair<ll,int> pii;
int mian(){tail = 0;if(scanf("%d",&n) != 1) return 0;for(int i = 1;i <= n;++i){ll t,h;scanf("%lld%lld",&t,&h);while(tail && hs[tail-1] <= h) tail--;ts[tail] = t;hs[tail] = h;tail++;}priority_queue<pii,vector<pii>,greater<pii> > Q;int st = 0;for(int i = 0;i < tail;++i){while(st < i && dp[st] <= ts[i]) st++;dp[i] = ts[i] + 2*hs[st];//初始化,解决dp第一部分while(!Q.empty()){pii p = Q.top();if(p.first <= ts[i] + 2*hs[p.second+1])Q.pop(); //对应题解里面处理小条件的方法else{dp[i] = min(dp[i],p.first);//解决dp第二步部分break; }}Q.push(make_pair(dp[i]+2*hs[i+1],i));}cout<<dp[tail-1]<<endl;return 1;
}int main(){while(mian());
}