• bzoj2448 挖油


    题意

    已知一个长度为n的数列,其中前x个数是1,后n-x个数是0.(x是未知的).
    可以付出ti的代价询问第i个数是1还是0.
    现在需要求x的值,采用最优策略,最坏情况下的代价是多少.

    分析

    定义f[i][j]表示已知i-1<=x<=j时,采取最优策略,直到查询出x的值的最坏代价.枚举下一次询问的位置k(i<=k<=j),那么(f[i][j]=min(t[k]+max(f[i][k-1],f[k+1][j])))
    显然f[i][k]随着k的增加是单调递增的,f[k][j]随着k的增加是递减的.所以对于[i,j]存在某个分界点p,使得对于所有(ile kle p),有(f[i][k]le f[k][j]),对所有(p<k<=j),有(f[i][k]gt f[k][j]).
    另一个结论是,对于所有长度为len的区间[i,i+len],随着i的递增,p的位置也是递增的.(可以认为现在有两条直线,一条斜率为正,一条斜率为负,现在把其中一条向上抬,另一条向下按,想象交点横坐标的移动方向).
    现在按照所有区间长度进行DP,先DP较短区间.
    这里我之前猜了不少结论,比如可以三分决策点,比如四边形不等式,比如决策单调性,没一个是对的,感受自己的愚蠢
    在DP时我们可以维护p.考虑k在p的左侧和右侧两种情况,就可以把方程里的max(f[i][k-1],f[k+1][j])干掉.
    现在我们分别需要查询t[k]+f[i][k-1]和t[k]+f[k+1][j]的最小值.这个东西可以写线段树,但据说会卡常数.既然我不会写zkw线段树,那就写树状数组好了23333.开2n个树状数组,分别对应n个左端点和右端点,例如L[i]这个树状数组里存储所有左端点为i的状态(f[i][i],f[i][i+1],f[i][i+2]...)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2005;
    int g[maxn][maxn],f[maxn][maxn];
    int n;
    int t[maxn];
    struct BIT1{
      int c[maxn];
      BIT1(){
        memset(c,0x7f,sizeof(c));
      }
      void add(int x,int w){
        for(;x;x-=x&(-x))c[x]=min(c[x],w);
      }
      int qmin(int x){
        int ans=0x7f7f7f7f;
        for(;x<maxn;x+=x&(-x))ans=min(ans,c[x]);
        return ans;
      }
    }C1[maxn];
    struct BIT2{
      int c[maxn];
      BIT2(){
        memset(c,0x7f,sizeof(c));
      }
      void add(int x,int w){
        for(;x<maxn;x+=x&(-x))c[x]=min(c[x],w);
      }
      int qmin(int x){
        int ans=0x7f7f7f7f;
        for(;x;x-=x&(-x))ans=min(ans,c[x]);
        return ans;
      }
    }C2[maxn];
    int main(){
      int n;scanf("%d",&n);
      for(int i=1;i<=n;++i)scanf("%d",t+i);
      for(int i=1;i<=n;++i){
        f[i][i]=t[i];
        if(i+1<=n)C1[i].add(i+1,f[i][i]+t[i+1]);
        if(i-1>=1)C2[i].add(i-1,f[i][i]+t[i-1]);
      }
      for(int len=1;len<n;++len){
        int pt=1;
        for(int i=1;i+len<=n;++i){
          int j=i+len;
          // f[i][j]=0x7f7f7f7f;
          // for(int k=i;k<=j;++k){
          //   f[i][j]=min(f[i][j],max(f[i][k-1],f[k+1][j]);
          // }
          while(f[i][pt-1]<f[pt+1][j])++pt;
          f[i][j]=min(C1[i].qmin(pt),C2[j].qmin(pt-1));
          if(j+1<=n)C1[i].add(j+1,f[i][j]+t[j+1]);
          if(i-1>=1)C2[j].add(i-1,f[i][j]+t[i-1]);
        }
      }
      printf("%d
    ",f[1][n]);
      return 0;
    }
    
    
  • 相关阅读:
    59、web框架以及Django框架
    58、bootstrap
    56、jQuery事件
    55、原生js事件与jQuery
    36-并发编程
    4.20---远程执行命令的CS架构软件
    35-socket 基于套接字的TCP与UDP
    34-网络编程
    33-异常处理
    4.15---元类练习
  • 原文地址:https://www.cnblogs.com/liu-runda/p/7106588.html
Copyright © 2020-2023  润新知