NOIP2017跳房子 二分+dp+单调队列

题目链接:https://ac.nowcoder.com/acm/contest/153/1004

题意

在这里插入图片描述

题解

首先知道如果所有的正数加起来小于k那么就输出-1,否则肯定有解。且解的大小在[1,x[n]-d]之间。这里可以二分答案。
对答案的判定用到了dp,dp[i]:前i格的最大分值,转移方程为:
dp[i] = max(dp[j]+s[i]), j < i ,同时满足j要在能跳的范围之内。因为n的范围是500000,所以二维dp肯定超时,这里找移动区间的最大值可以用单调队列,这样一次判断总的只要扫一遍就可以得出答案。
单调队列: 队头是当前区间的最大值
更新:对于要进队的元素,从队尾开始把小于进队元素的值都pop出来,然后在push进元素。然后要把队头超过可跳距离的元素pop出来。
这里要注意的是,进队的话只要距离大于跳跃的最小距离即可,不用小于最大距离,否则的话会导致后续元素无法进队。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
const ll INF = 1LL<<60;
ll x[maxn],s[maxn];
int n,d,k;
ll sum;
long long dp[maxn];
bool ok(int g) {
	int d1 = d, d2=d;
	if(g < d) {
		d1 = d-g; d2 = d+g;
	}else {
		d1 = 1; d2 = d+g;
	}
	deque<int> que;
	int cur = 0;
	dp[0] = 0;
	for(int i = 1; i <= n; ++i) {
		for(;cur < i &&  x[i]-x[cur]>=d1; ++cur) {
			if(que.empty())
				que.push_back(cur);
			else {
				while(!que.empty() && dp[que.back()] <= dp[cur])
					que.pop_back();
				que.push_back(cur);
			}
		}
		while(!que.empty() && x[i]-x[que.front()] > d2)
				que.pop_front();
		if(que.empty())
			dp[i] = -INF;
		else {
			dp[i] = dp[que.front()]+s[i];
		}
		if(dp[i] >= k)
			return true;
	}
	return false;
}
int main() {
	scanf("%d%d%d", &n, &d,&k);
	x[0] = s[0] = 0;
	for(int i = 1; i <= n; ++i) {
		scanf("%lld%lld",&x[i], &s[i]);
		if(s[i] > 0) {
			// suf[i] = s[i];
			sum += s[i];
		}
	}
	if(sum < k) {
		puts("-1");
		exit(0);
	}
	
	int r = x[n]-d;
	int l = 0,ans = -1;
	while(l <= r) {
		int mid = (l+r)>>1;
		if(ok(mid)) {
			ans = mid;
			r = mid-1;
		}else 
			l = mid+1;
	}
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值