zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    HDU 1261 字串数(排列组合)
    Codeforces 488C Fight the Monster
    HDU 1237 简单计算器
    POJ 2240 Arbitrage
    POJ 3660 Cow Contest
    POJ 1052 MPI Maelstrom
    POJ 3259 Wormholes
    POJ 3268 Silver Cow Party
    Codesforces 485D Maximum Value
    POJ 2253 Frogger(最短路)
  • 原文地址:https://www.cnblogs.com/wxyww/p/10313828.html
Copyright © 2011-2022 走看看