P3957 跳房子[二分答案][dp][单调队列]
Posted garen-wang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3957 跳房子[二分答案][dp][单调队列]相关的知识,希望对你有一定的参考价值。
P3957 跳房子
前年pj没去年难好吧
首先要发现这个答案是有单调性的。
这个很显然了:氪金越多游戏越容易玩,氪金越少越难。
然而也有界限:如果所有正数的和加起来还不够需求,无解。
所以二分答案,考虑如何判定答案。
是人都知道要设一个(dp[i])表示跳前(i)个房子的最大分数。
50pts就很简单的暴力转移:
[dp[i]=max(dp[j]+a[i]),j < i,lim_1<dis[i]-dis[j] < lim_2]
其实很大的暗示了:把(a[i])拿出来就是:(dp[i]=a[i]+max(dp[j]))
单调队列走一波,维护滑动窗口最大值!
不得不说这里的单调队列很难写对。
我们弄一个指针(j),当我们求(dp[i])的时候就移动(j)到最近。
然后当我们询问最大值的时候再考虑讨论退役人员的弹出,得到最大值。
代码:
#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
#define ll long long
const int maxn = 500005;
const int INF = 0x8f8f8f8f;
int a[maxn], dis[maxn];
int dp[maxn];
int n, d, k;
std::deque<int> q;// dan diao di jian
bool check(int g) {
int lim1 = std::max(1, d - g), lim2 = d + g;
memset(dp, 0x8f, sizeof dp);
dp[0] = 0;
q.clear();
int j = 0;
for(int i = 1; i <= n; i++) {
/*for(int j = 0; j < i; j++) {
if(dis[i] - dis[j] >= lim1 && dis[i] - dis[j] <= lim2) {
dp[i] = std::max(dp[i], dp[j] + a[i]);
}
}*/
while(j < i && dis[i] - dis[j] >= lim1) {
if(dp[j] != INF) {
while(!q.empty() && dp[q.back()] < dp[j]) q.pop_back();
q.push_back(j);
}
j++;
}
while(!q.empty() && dis[i] - dis[q.front()] > lim2) q.pop_front();
if(!q.empty()) dp[i] = dp[q.front()] + a[i];
}
int ans = 0x8f8f8f8f;
for(int i = 1; i <= n; i++) ans = std::max(ans, dp[i]);// wrong here
return ans >= k;
}
int main() {
cin >> n >> d >> k;
dis[0] = a[0] = 0;
for(int i = 1; i <= n; i++) {
cin >> dis[i] >> a[i];
}
int left = 0, right = std::max(dis[n], d), ans = -1;
while(left <= right) {
int mid = (left + right) >> 1;
if(check(mid)) ans = mid, right = mid - 1;
else left = mid + 1;
}
cout << ans << endl;
return 0;
}
以上是关于P3957 跳房子[二分答案][dp][单调队列]的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)
bzoj 2806: [Ctsc2012]Cheat广义SAM+二分+dp+单调队列