zoukankan      html  css  js  c++  java
  • nowcoder11254A Guess and lies(2021牛客暑期多校训练营3)dp

    题意

    Alice和Bob在玩猜数字游戏。开始,Alice选一个(1-n)之间的数(y)。Bob每次给Alice一个(x),问Alice是否有(yge x)。Alice有一次欺骗Bob的机会。Alice希望最大化轮数,Bob则希望最小化轮数。问对于(x_0=1,2,dots,n)(即Bob第一次问的值为(x_0)且Alice第一次回答为(yes)时),期望增加的轮数(第一次不算)是多少?((2le nle2000)

    分析

    首先,Bob有一个比较差的策略是每个点问两次,如果两次不同再问第三次,从而可以二分判断(y)的值,那么答案的上界即为(2lceil log(n) ceil+1)

    然后,我们看Bob的一次询问获得了什么信息。对于一次询问(x),如果Alice回答了(yes)则对所有真正答案在([1,x-1])的情况撒了一次谎;如果回答了(no)则对真正答案在([x,n])的情况撒了一次谎。

    所以我们可以记录一个(a)数组,(a_i)表示如果答案为(i)那么Alice撒了几次谎。显然Alice只能撒一次谎,那么(a)数组中值大于等于(2)的位置一定不是Alice一开始想的(y)。那么游戏就变为了Bob选取([1-n])的一个划分,Alice要把其中一部分加一,直到只出现一个位置的(a_ile1),游戏结束,(y)即为(i)

    转化后我们可以发现,在排除掉当前状态中所有的(ge2)的数后,剩下的一定是(a)(0)(b)(1)(c)(0)的情况((a,b,cge0))。初始的情况是Alice回答了(yes),然后形成了一个类似(111000)的串。然后对于每次询问(110011)可以划分在中间形成(221011)(110122),或者是划分在两端形成(210011)(121122)。其中(121122)可以看作(a=1,b=0,c=2)的情况,因为中间的(2)本质不影响。

    对于这个问题,首先可以可以列出一个(O(n^4))的dp:(dp[a][b][c])表示当前状态步数最小值,(dp[a][b][c]=min{max(dp[a-i][b][c],dp[i][0][b]),max(dp[i][b-i][c],dp[a][i][b-i]),max(dp[a][b][i],dp[b][0][c-i])}),即每次三种切割方式,两种赋值Alice取最大的那种结果。利用决策单调性可以优化到(O(n^3))

    考虑到第一维越长答案也越大,可以把第一维和答案交换。那么可以定义(dp[a][b][c])为答案为(a)(101)中有(b)(0)(c)(1)时,最长前导(1)有多长。然后又可以发现原式中前面的(1)和后面的(1)是等价的,可以通过枚举(a,b)找到一些前导(1)和后接(1)的pair。因为求的是最长的(1),所以大的pair是可以推出小的pair的,之后再求一个后缀最大值即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    constexpr int inf=1e9;
    constexpr int N(2005),A(25);
    int x01[A][N][N];
    int main() {
      for(int i=0;i<A;i++) for(int j=0;j<N;j++) fill(x01[i][j],x01[i][j]+N,-inf);
      x01[0][0][0]=1;
      x01[0][1][0]=0;
      x01[0][0][1]=0;
      for(int a=1;a<A;a++) {
        for(int m=0;m<N;m++) {
          vector<int>mx(N,-inf);
          for(int r=0;r<N;r++) { // cut 1
            int l=x01[a-1][m][r];
            l+=x01[a-1][0][m];
            if(l>=0) {
              l=min(l,N-1);
              mx[l]=max(mx[l],r);
              mx[r]=max(mx[r],l);
            }
          }
          for(int m1=0;m1<=m;m1++) { // cut 0
            int m2=m-m1;
            int l=x01[a-1][m1][m2];
            int r=x01[a-1][m2][m1];
            if(l>=0 && r>=0) {
              l=min(l,N-1);
              r=min(r,N-1);
              mx[l]=max(mx[l],r);
              mx[r]=max(mx[r],l);
            }
          }
          for(int r=N-2;r>=0;r--)
            mx[r]=max(mx[r],mx[r+1]); // transform from larger pair
          for(int r=0;r<N;r++)
            x01[a][m][r]=mx[r];
        }
      }
    
      int n;
      cin>>n;
      for(int m=n;m>=1;m--) {
        int ans;
        for(ans=0;ans<A;ans++) {
          if(x01[ans][m][n-m]>=0)
            break;
        }
        cout<<ans<<" 
    "[m==1];
      }
    
      return 0;
    }
    
  • 相关阅读:
    「初级篇」跟我一起学docker(二)--核心概念和安装
    程序员有哪些借口可以让自己写出低质量的代码?
    「初级篇」跟我一起学docker(一)--认识
    后端程序猿怎么提高技术?提高编码质量?
    河南这么大的省,也所谓的准一线,为什么IT行业就是发展不起来呢?
    JAVA使用Gson解析json数据,实例
    JAVA equals 和 “==”的异同
    JAVA WEB 对返回数据进行按中文名称首字母A~Z升序排序
    JAVA验证数字的正则表达式,来一发
    【转】Java.util.ArrayList.set()方法实例
  • 原文地址:https://www.cnblogs.com/intmian/p/15081340.html
Copyright © 2011-2022 走看看