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;
}
原文地址:https://www.cnblogs.com/intmian/p/15081340.html