• bzoj2448 挖油


    题目链接

    题面

    思路

    先考虑(n leq 100)的做法。
    区间dp。
    状态。用(f[l][r])表示知道l到r内(x)的位置最少需要的时间
    转移。枚举一个(l leq k leq r)。那么现在我们要在k处挖油了,然后我们根据k处有没有油再去确定下次是挖([k + 1,r])还是([l,k-1])。因为是在最坏情况下,所以我们挖完之后肯定是去(f[l][k - 1])(f[k + 1][r])之间较大的那个去挖。所以转移方程就是$$f[l][r] = min_{k=l}^r{max(f[l][k-1],f[k+1][r]) + w[k]}$$
    然后考虑优化上面的区间dp
    比较显然的一个性质就是(f[l][r] leq f[l][r+1],f[l][r] leq f[l-1][r])因为区间更大肯定不会使探测时间更少。
    然后观察上面的dp还有那些优化空间。发现枚举k的这一层循环是很浪费的。所以考虑能不能快速找出转移位置。
    然后继续观察那个暴力式子。发现我们从左到右枚举k的过程中,(f[l][k-1])单调不降,(f[k+1][r])单调不升,所以里面那个max肯定是前面一段全是的(f[k+1][r])后面一段全是(f[l][k-1])
    所以呢,肯定可以找到一个分界点(g),使得(g)前面从(f[l][k-1])转移,后面从(f[k+1][r])转移。
    然后怎么的到这个(g)呢。考虑我们固定l端点,然后从左向右枚举r端点。这时因为(g)右边在增大,所以(g)肯定是不断往右移动的。
    然后就可以用线段树来求区间最小值,并维护(g)。复杂度是(O(n^2logn))
    考虑继续优化
    其实可以用单调队列来优化。在向右枚举(r)的过程中,因为(g)是单调不降的,所以左边的点肯定会先失去转移的价值,所以维护出满足(f[l][k-1]+w[k]>f[k+1][r]+w[k])的点中的最大值,并且在不满足上面条件时出队。就可以了。这是以固定左端点考虑左边为例。固定右端点考虑右边也一样,只不过(r)是不断的跳的,所以要用n个单调队列来维护出(n)(r)端点时的转移位置。然后从这两个转移方案中选个更优的就行了。

    代码

    /*
    * @Author: wxyww
    * @Date:   2019-01-19 17:57:44
    * @Last Modified time: 2019-01-19 19:28:03
    */
    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<ctime>
    #include<bitset>
    using namespace std;
    typedef long long ll;
    const int N = 2000 + 10;
    #define A(x) f[l][x - 1] + w[x]
    #define B(x) f[x + 1][r] + w[x]
    ll read() {
       ll x=0,f=1;char c=getchar();
       while(c<'0'||c>'9') {
          if(c=='-') f=-1;
          c=getchar();
       }
       while(c>='0'&&c<='9') {
          x=x*10+c-'0';
          c=getchar();
       }
       return x*f;
    }
    ll f[N][N];
    int w[N],q[N][N],T[N],H[N];
    int main() {
       int n = read();
       for(int i = 1;i <= n;++i) w[i] = read();
       for(int l = n;l >= 1;--l) {
          H[0] = 1;
          T[0] = 1;
          f[l][l] = w[l];
          q[0][1] = l;
          T[l] = H[l] = 1;
          q[l][1] = l;
          for(int r = l + 1;r <= n;++r) {
             while(T[0] >= H[0] && A(q[0][H[0]]) < B(q[0][H[0]])) ++H[0];
             while(T[0] >= H[0] && A(r) < A(q[0][T[0]])) --T[0];
             q[0][++T[0]] = r;
             while(T[r] >= H[r] && B(q[r][H[r]]) < A(q[r][H[r]])) ++H[r];
             while(T[r] >= H[r] && B(l) < B(q[r][T[r]])) --T[r];
             q[r][++T[r]] = l;
             f[l][r] = min(A(q[0][H[0]]),B(q[r][H[r]]));
          }
       }
          cout<<f[1][n];
       return 0;
    }
    
  • 相关阅读:
    八爪鱼 爬取微博中的图片到本地
    【简易采集】美团数据抓取方法 八爪鱼
    jeesite 的提示消息图标
    SpringBoot 入门 Demo
    spring 简单入门实例
    正则表达式之匹配替换
    数据结构之堆栈
    c#设计模式之装饰者模式
    c#设计模式之策略模式
    一个自然数在1700和1800之间,且被5除余3,被7除余4,被11除余6,求符合条件的数
  • 原文地址:https://www.cnblogs.com/wxyww/p/10313828.html
Copyright © 2020-2023  润新知