[题] 跳房子 #dp #二分答案 #单调队列优化

题目

洛谷——P3957 [NOIP2017 普及组] 跳房子
ACwing——472. 跳房子

题意:
花最少的金币,获得分数k。
花g个金币可以升级机器人,使其跳跃距离范围向上向下增加g。
求满足分数下,最少使用金币数。


题解

方法一:贪心直接剪枝。
博客跳转:[题] 跳房子 #dp #二分答案

方法二:单调队列优化。
注意事项:

  1. 首先点名卡了我一天多的错误的点:单调队列出入队操作的先后顺序。
    在这道题里面一定是先入队再出队。因为先出队的话,后续可能会让不在范围内的点进队没被出队,甚至直接排到队头影响当前点的答案,结果就错了。(例子没想到,举不出来……)
    反过来,先把能入队的先入了,然后一起筛出队伍,保证队头一定是在范围里面的就不会出错了。
  2. 范围。这道题之所以难,有一点就在范围上面,因为二分和单调队列都是对范围要求极其精准的算法,稍有不慎就会WA掉……

代码

#include 
//很大的数,初始化加个负号就当是负无穷了
#define INF 0x7f7f7f7f 
using namespace std;
typedef long long LL;
const int N = 500010;
int n, d, k, L = 0, R = 20000, ans = -1, mid;
//R的大小可以通过x/n来确定,平均距离为2000
//f[i]是指到i点能获得的最高分。
//q是单调队列。
LL dist[N], w[N], f[N], q[N];
//检查花费g个金币进行改造后,最高得分是否会超过k。
bool check(int gold) {
    memset(q, 0, sizeof q);
	f[0] = 0;
	//初始化为负无穷,因为格子上的分数有负数(神奇吧?)。
	for(int i = 1; i <= n; i ++)
		f[i] = -INF;
    //机器人跳跃的弹性范围
    int minn = max(1, d - gold), maxx = d + gold;
    //st是头,ed是尾, nxt是下一个入队的元素。
    //注意nxt是从起点开始的
    int st = 0, ed = -1, nxt = 0;
    //目的格子
    for(int i = 1; i <= n; i ++) {
        //维护区间[lf,rt]
        int rt = dist[i] - minn, lf = dist[i] - maxx;
        //这一步可以用单调队列维护
        //区间为lf~rt
        //先入队:对在不超过范围右端的元素进行入队
        while(dist[nxt] <= rt) {
        	//注意该元素的f值不可为负无穷,这点的f值是负无穷的话
        	//说明前面的点不能跳到这点,自然就不能从这点跳到后面的点。
        	if(f[nxt] > -INF) {
        		//单调性不满足且队伍里有元素。
	            while(f[nxt] >= f[q[ed]] && ed >= st)
	            	//队尾出队
	                ed --;
	            //从队尾加入新元素。
	            q[++ ed] = nxt;
			} 
            nxt ++;
        }
        //再出队:对超出范围左端的元素进行出队。
        while(dist[q[st]] < lf && st <= ed)
            st ++;
        if(st <= ed)
        	f[i] = w[i] + f[q[st]];
        //完成目标。
        if(f[i] >= k)
            return true;
    }
    return false;
}
int main() {
    scanf("%d%d%d", &n, &d, &k);
    for(int i = 1; i <= n; i ++)
        scanf("%lld%lld", &dist[i], &w[i]);
    while(L < R) {
        //所有满足条件的情况都在mid的右边区间,
        //搜索左边区间最小值
        mid = L + R >> 1;
        if(check(mid)) {
            ans = mid;
            R = mid;
        }
        else L = mid + 1;
    }
    cout << ans;
    return 0;
}

你可能感兴趣的:(算法,c++)