zoukankan      html  css  js  c++  java
  • P2678 跳石头

    答案具有单调性,即存在一个最优解(最短条约距离的最大值),使得大于此解值的值都需要移走多于M个石头,而且所有小于此解值的所有值都是可行解(但他们不是最优的,所以不是答案). 

    可以先假定一个值,检测他是否为可行解,并且通过多次这样的假定得出最优解.

    使用函数bool C(x),若解x可行则返回true,否则返回false.

    根据答案的单调性,接下来通过二分来假定答案就可以求解.二分的写法:

        int l = 0, r = 1000000000;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (C(mid))
                l = mid;
            else
                r = mid - 1;
        }

    此时l(l == r)即为最优解.

    注意到,这里 l = mid; r = mid - 1; 是根据实际问题选择的,在这里含义是如果mid值可行,那么在mid及其右侧可能有比mid更优的解(当然,首先得有可行解,此时可行即比mid更优).

    如果mid值不可行,显然mid及其右侧均无可行解,不需要考虑这一区间了.

    出于上述考虑,会写出:

    if (C(mid))
        l = mid;
    else
        r = mid - 1;

    而一旦你的问题采用了这种形式,就必须有:

    int mid = l + r + 1 >> 1;

    才能保证不会陷入死循环.

    AC代码:

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    
    int L, N, M, s[50010];
    
    // 思路: s[0]为0, 即起点, s[N + 1]为L, 即终点
    // 对于s[i], 检查其前方有多少个石头是在条约距离x以内(不含)的, 每有一个石头则移除之, 并且i递增以在下一次外层循环中跳过已经视为移除的石头
    bool C(int x) { int ct = 0, tmp; for (int i = 0; i <= N; i++) {    // 这里的写法并不好, 可以写出O(n)的C(x), 参考洛谷题解区, 点击文首图片跳转 tmp = 0; for (int j = 1; j + i <= N + 1 && s[i + j] - s[i] < x; j++) tmp++; ct += tmp; i += tmp; } return ct <= M; } int main() { cin >> L >> N >> M; for (int i = 1; i <= N; i++) cin >> s[i]; s[N + 1] = L; int l = 0, r = 1000000000; while (l < r) { int mid = l + r + 1 >> 1; if (C(mid)) l = mid; else r = mid - 1; } cout << l << endl; return 0; }

    在这题以外,如果以后根据实际问题写出了:(仅对于整数)

    if(...)
        l = mid + 1;
    else
        r = mid;

    那么必须有:

    int mid = l + r >> 1;

    以保证不会陷入死循环.

  • 相关阅读:
    抽象类、接口
    静态联编、动态联编
    多态
    QML、Qt Quick
    重载、多态、继承、封装
    异步消息postEvent更新界面
    联合体union
    使用 Git@OSC 管理代码
    excel数据生成sql insert语句
    excel数据生成sql insert语句
  • 原文地址:https://www.cnblogs.com/Gaomez/p/14156722.html
Copyright © 2011-2022 走看看