zoukankan      html  css  js  c++  java
  • [基础博弈论练习题](性质(神仙)题 + dp)

    (基础个鬼)

    题意:小A和小B在玩游戏,假设一个长度为n的数组,小A想到一个数x,小B每次猜一个数y让小A回答x >= y?小A最多可以撒一次慌,小B希望最小化询问的次数来确定x,小A希望最大化小B问的次数.对于每个u,假设小B第一次询问的是u,且小A回答是(有可能撒谎),且接下来两者都采取最优策略的情况下,你需要求出接下来至少还要几轮才能确定答案.

    n <= 2e3

    发现是否撒谎这一点特别难处理.我们考虑改写游戏来处理是否撒谎这个问题,设a[y]表示如果小A想的是y,那么小A还能撒谎多少次.显然初始a[y] = 1且当a[y] < 0时,y便不可能是小A想的数.考虑每次回答,与小A回答向左的a[y]就是减少1(即如果回答x >= y为真则任意u < y 有 a[u]--).考虑小B当且仅当仅存在一个a[y] >= 0时,小B可以确定答案.

    容易发现只有非负的a我们需要处理,又因为每次减少的都是前缀一段,或者后缀一段.所以我们发现>=0的a一定满足(就是把所有小于0的a都删掉,重新组成一段连续的)u个0,v个1,然后w个0

    于是我们可以设计dp[u][v][w]表示在这种局面下,还需多少步才能猜出答案

    dp转移方程如下:

    int DP(int u,int v,int w){
        if(dp[u][v][w] != inf)    return dp[u][v][w];
        for(int i = 2; i <= u + v + w; ++i){
            if(i <= u){
                int x = i;
                cmin(u,v,w,max(DP(u-x+1,v,w),DP(x-1+v,0,0)) + 1,i);
            }
            else if(i <= u + v){
                int x = i - u;
                cmin(u,v,w,max(DP(x-1,v-x+1,w),DP(u,x-1,v-x+1)) + 1,i);
            }
            else{
                int x = i - u - v;
                cmin(u,v,w,max(DP(0,0,w-x+1+v),DP(u,v,x-1)) + 1,i);
            }
        }
        return dp[u][v][w];
    }

    即枚举小B猜哪里,后面那个max分别对应小A说否,和小A说是,因为小A希望最大化,所以取max

    这样的时间复杂度是n ^ 4的,显然不能接受

    注意到值域很小是log级别的,我们考虑改写DP,设f[u][v][k] 表示最大的w,满足dp[u][v][w] <= k

    注意到如果小A回答否,则接下来的转移与w无关,我们考虑先固定u,v,k算出只转移左边,最远能到哪里(相当于算出决策点在哪里)

    如果决策点知道了那就很好办了,因为我们只要考虑决策点x在u段内还是v段内,就可以照着上面dp方程直接转移了,即

                    if(x <= i)        dp[k][i][j] = max(dp[k][i][j],dp[k-1][i-x+1][j]);/*照着上面的dp方程写*/
                    else if(i + j - x + 1 >= 0)    dp[k][i][j] = max(dp[k][i][j],dp[k-1][x-i-1][i+j-x+1]);

    此处i,j表示u,v,dp是改写后的,即题解中的f数组

    然后问题瓶颈就在于固定u,v如何快速算出决策点x

    暴力枚举显然是n^3的,仍然无法通过2000的数据

    我们继续观察朴素的dp方程

    if(i <= u)
       -> DP(x-1+v,0,0)
    else if(i <= v)
        -> DP(u,x-1,v-x+1)

    我们发现对于一个固定的u和k,v越大,决策点反而越要向左;

    为什么呢

      dp(x-1+v,0,0)就很显然了(x + v要一样,v越大,x越小)

    至于第二个,感性理解一下,肯定是a[y] = 1的比较少,需要的次数比较少

    于是我们得到了一个决策的单调性 p[k][u][v] >= p[k][u][v + 1]

    然后就可以O(n ^ 2log)做题了

    完整代码如下

    /*A.[21省选day1]基础博弈练习题*/
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    int read(){
        char c = getchar();
        int x = 0;
        while(c < '0' || c > '9')        c = getchar();
        while(c >= '0' && c <= '9')        x = x * 10 + c - 48,c = getchar();
        return x;
    }
    const int N = 2010;
    int dp[21][N][N],n;
    int p[21][N][N];
    #define inf 0x3f3f3f3f
    /*int DP(int u,int v,int w){
        if(dp[u][v][w] != inf)    return dp[u][v][w];
        for(int i = 2; i <= u + v + w; ++i){
            if(i <= u){
                int x = i;
                cmin(u,v,w,max(DP(u-x+1,v,w),DP(x-1+v,0,0)) + 1,i);
            }
            else if(i <= u + v){
                int x = i - u;
                cmin(u,v,w,max(DP(x-1,v-x+1,w),DP(u,x-1,v-x+1)) + 1,i);
            }
            else{
                int x = i - u - v;
                cmin(u,v,w,max(DP(0,0,w-x+1+v),DP(u,v,x-1)) + 1,i);
            }
        }
        return dp[u][v][w];
    }*/
    void solve(int x){
        for(int j = 0; j < 17; ++j){
            if(dp[j][x-1][n-x+1] >= 0){
                printf("%d ",j);
                break;
            }
        }
    }
    bool check(int k,int i,int j,int p){
        if(p <= i)    return dp[k-1][p-1][0] >= j;
        else    return dp[k-1][i][p-i-1] >= j + i - p + 1;
    }
    int main(){
        n = read();memset(dp,0xcf,sizeof(dp));
        dp[0][0][0] = 1;dp[0][1][0] = dp[0][0][1] = 0;
        for(int k = 1; k < 17; ++k){
            for(int i = 0; i <= n; ++i){
                for(int j = n - i; ~j; --j){/*越往左转移点反而越大,仔细观察dp方程,越往右,1的那段会转移过来,越难满足*/
                    dp[k][i][j] = (dp[k-1][i][j] + (1 << (k - 1)) - j);
                    p[k][i][j] = ((j == 0)?1:p[k][i][j+1]);
                    while(p[k][i][j] < i + j && check(k,i,j,p[k][i][j] + 1))    p[k][i][j]++;
                    int x = p[k][i][j];
                    if(x <= i)        dp[k][i][j] = max(dp[k][i][j],dp[k-1][i-x+1][j]);/*照着上面的dp方程写*/
                    else if(i + j - x + 1 >= 0)    dp[k][i][j] = max(dp[k][i][j],dp[k-1][x-i-1][i+j-x+1]);
                }
            }
        }
        for(int i = 1; i <= n; ++i)        solve(i);
        return 0;
    }
  • 相关阅读:
    B.Little Sub and Triples
    A.Little Sub and Applese
    1003 超级无敌简单题[暴力打表]
    1002 人类史上最大最好的希望事件[前缀和]
    1001 hzy 和zsl 的生存挑战
    Computer Arrangement [经典贪心]
    Linux指令
    LInux
    Servlet
    El表达式与JSTL表达式
  • 原文地址:https://www.cnblogs.com/y-dove/p/14254502.html
Copyright © 2011-2022 走看看