• [题] 跳房子 #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
  • 相关阅读:
    端口号,UDP,TCP
    学习python第一天(数据类型)
    华为云云耀云服务器L实例评测 | L实例性能测试实践
    【KAWAKO】从mac上定时将腾讯云的数据备份到本地
    LCR 174.寻找二叉搜索树中的目标节点
    前端设计模式
    IDaaS 系统 ArkID 一账通内置插件:图形验证码认证因素的配置流程
    10-css宽高自适应&伪元素
    jsp如何读取数据库,取到数据后,展示数据。
    Oracle(13)Maintaining Data Integrity
  • 原文地址:https://blog.csdn.net/weixin_45916959/article/details/134053908