潜龙未见静水流,沉默深藏待时秋。一朝破空声势振,惊世骇俗展雄猷。

LOJ3831 「IOI2022」囚徒挑战 题解

题目描述

这是一道交互题。

有两个袋子 \(A\)\(B\) ,装有不同数量的硬币,保证硬币数量 \(\le n\)

任意时刻白板上都写有一个数,初始为 \(0\)

接下来有若干(不超过 \(n\) )个人,依次进行如下操作:

  • 记白板上的数为 \(i\)根据 s[i][0] 决策看哪个袋子,记袋中硬币个数为 \(j\)
  • 指认哪个袋子硬币数量较少,或将白板上的数改为 s[i][j]

你的目标是构造 \(x\times(n+1)\) 的二维数组 s[i][j] ,使得这些人能够正确判断出哪个袋子硬币数量较少。

其中 s[i][0] 的值必须为 01 ,分别表示看 \(A\)\(B\)

\(1\le j\le n\) ,若 s[i][j] 的值为 -1-2 ,则分别表示指认 \(A\)\(B\) 数量较少,否则 s[i][j] 应当是一个 \(\le x\) 的非负整数,表示这个人在白板上写下的数。

你需要实现以下函数:

  • vector<vector<int>> devise_strategy(int N);

    返回你构造的二维数组。

数据范围

  • \(1\le n\le 5000\)
  • 你可以自行决定 \(x\) 的值,但需要保证 \(x\le 20\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{2048MB}\)

分析

\(A\) 袋硬币数量为 \(a\)\(B\) 袋硬币数量为 \(b\) ,目标比较 \(a\)\(b\) 的大小。

考虑二进制拆分,第一个人看 \(a\) 并写下最高位,第二个人看 \(b\) 后将最高位与 \(a\) 比较,如果不同则直接出结果,如果相同则写下次高位。

在构造 s 数组时,每两个人为一组,通过组的编号可以知道当前正在处理哪一位,组内分别处理上一位写下的是 \(0/1\) 的情况。

于是我们得到了一个 \(x=2\lceil\log_2n\rceil=26\) 的做法。如果换成三进制,可以得到 \(x=3\lceil\log_3n\rceil=24\)

有一个小优化:由于 \(a\neq b\) ,所以看完最后一位可以直接出结果。 \(x\) 降至 \(2\lceil\log_2n\rceil-2=24\)

事实上这个优化还可以推广,假设当前已经确定 \(a,b\in[l,r]\) ,那么如果看到 \(l\)\(r\) 则可以直接出结果。

\(f_n\) 表示值域为 \(n\)\(x\) 的最小值。根据上面这个优化,我们可以把原来的转移方程 \(f_n=\min f_{\lceil\frac nk\rceil}+k\) 改成 \(f_n=\min f_{\lceil\frac{n-2}k\rceil}+k\)

打表发现刚刚好 \(f_{5000}=20\) ,一条可行的路径(从高到低)为 \(3,3,3,3,2,2,2,2\)

递归实现,时间复杂度 \(\mathcal O(nx)\)

#include<bits/stdc++.h>
#include"prison.h"
using namespace std;
const int maxn=5005;
int n;
vector<vector<int>> s;
void dfs(int dep,int l,int r,int L,int R,int i,int lim)
{///当前第dep层,保证另一个数属于[l,r],自己属于[L,R],白板上的数为i,已经使用0~lim的编号
    if(l>r) return ;
    int k=dep<=4?3:2,len=(r-l-1+k-1)/k;
    ///注意同一个i会被dfs到多次,但是只处理j\in[L,R]的情况
    s[i][0]=dep&1?0:1;
    for(int j=L;j<=l;j++) s[i][j]=dep&1?-1:-2;
    for(int j=r;j<=R;j++) s[i][j]=dep&1?-2:-1;
    for(int t=1,x=l+1,y;x<=r-1;t++,x=y+1)
    {///第t部分为[x,y]
        y=min(x+len-1,r-1);
        for(int j=x;j<=y;j++) s[i][j]=lim+t;
        dfs(dep+1,x,y,l,r,lim+t,lim+k);
    }
}
vector<vector<int>> devise_strategy(int _n)
{
    n=_n,s.resize(21);
    for(int i=0;i<=20;i++) s[i].resize(n+1);
    dfs(1,1,n,1,n,0,0);
    return s;
}

posted on 2023-01-18 22:11  peiwenjun  阅读(9)  评论(0)    收藏  举报

导航