• 【2018 1月集训 Day1】二分的代价


    题意:

      现在有一个长度为 n的升序数组 arr 和一个数 x,你需要在 arr 中插入 x

      你可以询问 x 跟 arri 的大小关系,保证所有 arri x 互不相同。这次询问的代价为 costi

      你需要返回 x 应该插入的位置,显然有 n+1 中可能的返回值。

      现在给你 cost 数组,你需要制定方案,使得对于所有可能的情况花费代价(即询问的代价的和)的最大值最小,输出这个最小值。

      制定方案的意思就是说你先询问一个 i,然后根据返回值决定接下来询问哪个 i,直到你可以确定答案为止。

     

    分析:

      这个题好神啊……我看了ztb大爷的代码……可能我理解的也不是很准确啊……那就三个月后再战此题吧……

      首先可以看到,每个ai不超过9,所以最终的答案一定不大,最变态的上界也不过就是9logn,但是应该达不到。

      我们设两个dp数组:

        f[i][v]表示以i为当前区间的左端点,花费为v,最长能确定的区间的右端点

        g[i][v]表示以i为当前区间的右端点,花费为v,最长能确定的区间的左端点。(其实就是对称的)

      那么我们可以看到,对于一个costi,假如我们付出这样的代价,那么在暂时不考虑左端点的情况下,最长的区间的右端点一定在f[i+1][v-costi],那我们该用这个点去更新哪个状态呢???

      即为这个可行区间找一个最左端点,那么我们另一个数组就派上用场了,花费已经确定,那么我们就令:

      f[g[i][v-costi]][v]=max(f[g[i][v-costi]][v], f[i+1][v-costi]);

      或者换一种表达方式,就是在i这个点,我们花costi的花费,使总花费达到v,可以使g[i][v-costi]表示的这个点到f[i+1][v-cost]表示的这个点的区间可行(这段区间最多用v的代价就可以检索到每一个值)

      你看到我们的方程,发现i及i之前有一个花费v-costi达到的最远区间,i之后也是花费v-costi达到的最远区间,会不会两遍加起来的花费就超过了v-costi呢?

      如果你这样想,说明你的理解和这道题的题意真的是阴阳两隔没错就是我)。

      题目里说的是根据返回的值来调整继续下去的决策,所以说,这一步操作就相当于是,假如我们询问了i,那么返回的参数如果是小于等于ai,我们就走左边那个区间来花掉剩下的v-costi,如果返回值告诉我们,我们的x大于ai,那么我们就往右查找,来花掉剩下的v-costi,根本不存在v-costi花两遍的情况,因为根本不可能既走左边又走右边!

      另一个方程同理:g[f[i+1][v-costi]][v]=min(g[f[i+1][v-costi]][v],g[i][v-costi]);

      当然,初始化就是f[i][v]=g[i][v]=i;

      但是,有些costi由于太过不优,我们决策时会直接跳过,但是它会被之前某些决策所覆盖,所以也是需要更新的,于是就多了两个for循环来保证所有答案合法且最优。

      当f[1][v]覆盖整个区间时,v就是题目的答案。

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=100005,M=140;
     4 int a[N],f[N][M],g[N][M],n;char s[N];
     5 int main(){
     6     scanf("%s",s+1);n=strlen(s+1);
     7     for(int i=1;i<=n;i++) a[i]=s[i]-'0';
     8     for(int v=0;v<M;v++)
     9     for(int i=1;i<=n+1;i++)
    10     f[i][v]=g[i][v]=i;
    11     for(int v=1;v<M;v++){
    12         for(int i=1;i<=n;i++){
    13             if(v<a[i]) continue;
    14             f[g[i][v-a[i]]][v]=max(
    15             f[g[i][v-a[i]]][v],f[i+1][v-a[i]]);
    16             g[f[i+1][v-a[i]]][v]=min(
    17             g[f[i+1][v-a[i]]][v],g[i][v-a[i]]);
    18         } for(int i=2;i<=n+1;i++)
    19         f[i][v]=max(f[i][v],f[i-1][v]);
    20         for(int i=n;i;i--)
    21         g[i][v]=min(g[i][v],g[i+1][v]);
    22         if(f[1][v]==n+1) 
    23         {printf("%d
    ",v);return 0;}
    24     } return 0;
    25 }
    dp

      

  • 相关阅读:
    c语言之排序
    c语言中的break 和 continue语句
    c语言之循环
    c语言之选择
    使用函数封装代码
    C语言的运算符
    判断两个对象是否相等:hashcode
    更新线上的资源存在删除和添加的情况-要避免空窗期的实现方法
    变量只能设置一次方法
    JAVA-获取系统信息:内存和系统、PID、内核
  • 原文地址:https://www.cnblogs.com/Alan-Luo/p/10210328.html
Copyright © 2020-2023  润新知