Loading

CF1028G Guess the number 解题报告

包含本题的交互题单

这个也是模板题吧。

首先题目很明显就是让我们找一个划分决策树的策略。

但是和二分、三分不同,这次我们可以划分的份数直接受到区间的 \(l\) 的影响,这使得直接数学构造非常麻烦。

我会 DP!设 \(f[l][r]\) 代表在 \([l,r]\) 这个区间中完成游戏所需要的最少操作次数,但是显然数据太大。

然后就是不难发现 \(1 \le k \le 10^4\),直接这样描述状态太浪费了。先把第一维改成目前可以询问的最长数组长度。

想了想发现直接把第二维改成目前剩余的询问机会比较好,这样就可以 DP 了,转移是显然的。

然后我们发现直接 DP 是 \(\mathcal{O}(k^2)\),这不好。但是又不难发现,可以剪枝,DP 代码如下。

const int N = 1e4 + 7;
int f[N][7];
int dfs(int l, int ch){
  if(ch == 0) return f[l][ch] = 0;
  if(f[l][ch] != -1) return f[l][ch];
  int cur = min(l, 10000ll);
  int res = dfs(cur, ch - 1);
  f[l][ch] = res;
  cur = min(10000ll, l + res + 1);
  for(int i = 1; i <= l; i ++){
    res = dfs(cur, ch - 1);
    if(cur >= 10000){
      f[l][ch] += (res + 1) * (l - i + 1);
      break;
    }
    f[l][ch] += res + 1;
    cur = min(10000ll, cur + res + 1);
  }
  return f[l][ch];
}

然后就跑得飞快了。

然后询问就是模仿转移就可以了,对于有的题可以直接把转移的状态存下来,这题就不必了。

signed main(){
  ios::sync_with_stdio(0), cin.tie(0);
  memset(f, -1, sizeof f);
  dfs(1, 5);
  int L = 1, R = 10004205361450474;
  for(int ch = 5; ch; ch --){
    vector<int> qur;
    int l = L;
    l += f[min(10000ll, l)][ch - 1];
    l --;
    while(l < R){
      qur.push_back(++ l);
      l += f[min(10000ll, l + 1)][ch - 1];
    }
    int k = qur.size();
    cout << k << " ";
    for(int x: qur) cout << x << " ";
    cout << endl;
    int res; cin >> res;
    if(res == -1 || res == -2) return 0;
    else{
      if(res == 0) R = qur[0] - 1;
      else if(res == k) L = qur.back() + 1;
      else L = qur[res - 1] + 1, R = qur[res] - 1;
    }
  }
  return 0;
}
posted @ 2026-05-20 13:25  Trent900  阅读(25)  评论(0)    收藏  举报