P10802 [CEOI2024] 核酸检测 题解
Description
Solution
Sub1 可以直接对于每个每个人问一次,考虑 Sub2 怎么做。
首先有个显然的想法是二分出第一个阳性的人,次数大概是
注意到每个位置的状态是随机生成的,并且题目要求的次数非常优,所以可以考虑利用期望 dp 求出最小期望操作次数。
设
对于
对于
对于
经过测试,当
输出方案可以通过 dp 过程中的最优转移点来做。
时间复杂度:
Code
#include <bits/stdc++.h> // #define int int64_t const int kMaxN = 1e3 + 5; int n; int trans[kMaxN][kMaxN]; double p, pw[kMaxN], f[kMaxN][kMaxN]; std::mt19937 rnd(std::random_device{}()); bool test_students(std::vector<bool> mask) { assert(mask.size() == (size_t)n); std::string mask_str(n, ' '); for (int i = 0; i < n; i++) mask_str[i] = mask[i] ? '1' : '0'; printf("Q %s\n", mask_str.c_str()); fflush(stdout); char answer; scanf(" %c", &answer); return answer == 'P'; } bool ask(std::vector<int> vec) { std::vector<bool> mask(n); for (auto x : vec) mask[x] = 1; return test_students(mask); } void del(std::vector<int> &v1, std::vector<int> &v2) { static bool vis[kMaxN] = {0}; for (auto x : v1) vis[x] = 1; for (auto x : v2) vis[x] = 0; std::vector<int> tmp; std::swap(v1, tmp); for (auto x : tmp) { if (vis[x]) v1.emplace_back(x); vis[x] = 0; } } void solve(std::vector<int> v1, std::vector<int> v2, std::vector<bool> &answer) { static bool vis[kMaxN] = {0}; int a = (int)v1.size(), b = (int)v2.size(); if (!a) return; if (b == 1) { answer[v2[0]] = 1; del(v1, v2); solve(v1, {}, answer); } else if (!b) { int k = trans[a][b]; std::vector<int> vec; for (int i = 0; i < k; ++i) vec.emplace_back(v1[i]); if (!ask(vec)) { del(v1, vec); solve(v1, {}, answer); } else { solve(v1, vec, answer); } } else { int k = trans[a][b]; std::vector<int> vec; for (int i = 0; i < k; ++i) vec.emplace_back(v2[i]); if (!ask(vec)) { del(v1, vec), del(v2, vec); solve(v1, v2, answer); } else { solve(v1, vec, answer); } } } std::vector<bool> find_positive(bool op) { if (!op) { std::vector<bool> answer(n); for (int i = 0; i < n; ++i) { std::vector<bool> vec(n); vec[i] = 1; answer[i] = test_students(vec); } return answer; } else { std::vector<int> vec; std::vector<bool> answer(n); for (int i = 0; i < n; ++i) vec.emplace_back(i); solve(vec, {}, answer); return answer; } } void prework() { pw[0] = 1; for (int i = 1; i <= n; ++i) pw[i] = pw[i - 1] * (1 - p); for (int i = 1; i <= n; ++i) { f[i][0] = 1e18, f[i][1] = f[i - 1][0]; for (int j = 2; j <= i; ++j) { f[i][j] = 1e18; for (int k = 1; k <= j; ++k) { double pr = (pw[k] - pw[j]) / (1 - pw[j]); // 选的 k 个没有的概率 double val = pr * f[i - k][j - k] + (1 - pr) * f[i][k] + 1; if (val < f[i][j]) { f[i][j] = val, trans[i][j] = k; } } } for (int j = 1; j <= i; ++j) { double pr = pw[j]; double val = pr * f[i - j][0] + (1 - pr) * f[i][j] + 1; if (val < f[i][0]) { f[i][0] = val, trans[i][0] = j; } } } } int32_t main() { int T; scanf("%d %lf %d", &n, &p, &T); if (T > 1) prework(); for (int i = 0; i < T; i++) { std::vector<bool> answer = find_positive(T > 1); assert(answer.size() == (size_t)n); std::string answer_str(n, ' '); for (int j = 0; j < n; j++) answer_str[j] = answer[j] ? '1' : '0'; printf("A %s\n", answer_str.c_str()); fflush(stdout); char verdict; scanf(" %c", &verdict); if (verdict == 'W') exit(0); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-09-21 HDU3085 Nightmare Ⅱ 题解
2021-09-21 P4591 [TJOI2018]碱基序列 题解