zoukankan      html  css  js  c++  java
  • 洛谷P3957 跳房子 题解 二分答案/DP/RMQ

    题目链接:https://www.luogu.org/problem/P3957
    这道题目我用到了如下算法:

    • 线段树求区间最大值;
    • 二分答案;
    • DP求每一次枚举答案g时是否能够找到 (ge k) 的解法。

    我们一开始用 (x[i])(s[i]) 来表示到起点的距离以及第 (i) 个点的分值。
    与此同时我们还要算上我们的起点,它满足性质 (x[0] = s[0] = 0) ,我们接下来的判断都是建立在这 (1 + n) 个点的基础上的。

    check(g)

    首先,我们假设 (g) 已经确定的情况下,如何判断是否有行走方案能够累计到 (ge k)
    这一步需要用到DP思想,我们定义状态 (f[i]) 表示从起点到第 (i) 个点所能够积累的最大分值,那么,状态转移方程为:
    (f[i] = max( f[j] )) ,其中 (j) 满足 (x[i]-d-g le x[j] le min( x[i]-1, x[i]-d+g ))
    并且我们可以发现这个范围的 (j) 必定在一个连续的区间 ([L, R]) 内,所以我们可以用二分(lower_boundupper_bound 函数来快速获得 (L)(R)
    然后我们需要知道区间 ([L,R]) 范围内 (x[j]) 的最大值,这一步过程我是使用线段树来实现的(因为这里涉及单点更新及区间最值)。

    二分答案

    在编写完 check(g) 函数之后,我们便可以在区间 ([0, x[n] ]) 范围内进行二分,进而找到满足要求的最小的答案。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 500050;
    int n, d, k, x[maxn], s[maxn], f[maxn];
    
    #define lson l, mid, (rt<<1)
    #define rson mid+1, r, (rt<<1|1)
    #define inf (1<<29)
    int maxv[maxn<<2];
    void sg_push_up(int rt) {
        maxv[rt] = max(maxv[rt<<1], maxv[rt<<1|1]);
    }
    void sg_build(int l, int r, int rt) {
        if (l >= r) maxv[rt] = -inf;
        else {
            int mid = (l + r) / 2;
            sg_build(lson);
            sg_build(rson);
            sg_push_up(rt);
        }
    }
    void sg_update(int p, int v, int l, int r, int rt) {
        if (l == r) maxv[rt] = v;
        else {
            int mid = (l + r) / 2;
            if (p <= mid) sg_update(p, v, lson);
            else sg_update(p, v, rson);
            sg_push_up(rt);
        }
    }
    int sg_query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) return maxv[rt];
        int mid = (l + r) / 2, tmp = -inf;
        if (L <= mid) tmp = max(tmp, sg_query(L, R, lson));
        if (R > mid) tmp = max(tmp, sg_query(L, R, rson));
        return tmp;
    }
    
    bool check(int g) {
        sg_build(0, n, 1);
        sg_update(0, 0, 0, n, 1);
        for (int i = 1; i <= n; i ++) {
            f[i] = -inf;
            int lid = lower_bound(x, x+i, x[i]-d-g) - x;
            int rid = upper_bound(x, x+i+1, min(x[i]-1, x[i]-d+g)) - x - 1;
            if (lid <= rid) {
                int tmp = sg_query(lid, rid, 0, n, 1);
                if (tmp != -inf) {
                    f[i] = tmp + s[i];
                    if (f[i] >= k) return true;
                    sg_update(i, f[i], 0, n, 1);
                }
            }
        }
        return false;
    }
    
    int main() {
        cin >> n >> d >> k;
        for (int i = 1; i <= n; i ++) cin >> x[i] >> s[i];
        int L = 0, R = x[n], res = -1;
        while (L <= R) {
            int mid = (L + R) / 2;
            if (check(mid)) { res = mid; R = mid-1; }
            else L = mid + 1;
        }
        cout << res << endl;
        return 0;
    }
    

    作者:zifeiy

  • 相关阅读:
    bzoj 1030 [JSOI2007]文本生成器
    Swift 学习笔记 (闭包)
    Swift 学习笔记 (函数)
    HTML 学习笔记 JQueryUI(Interactions,Widgets)
    HTML 学习笔记 JQuery(表单,表格 操作)
    HTML 学习笔记 JQuery(animation)
    HTML 学习笔记 JQuery(盒子操作)
    HTML 学习笔记 JQuery(事件)
    HTML 学习笔记 JQuery(DOM 操作3)
    HTML 学习笔记 JQuery(DOM 操作2)
  • 原文地址:https://www.cnblogs.com/codedecision/p/11753024.html
Copyright © 2011-2022 走看看