XXI Open Cup, Grand Prix of Nizhny Novgorod B 题解

First Post:

Last Update:

赛时以为题意是个纳什均衡,写完之后第三个样例卡了很久。这种博弈论能不能把策略模型写清楚啊。/fn

题意:

道题,第 道题分数为 ,你和 Tourist 一起做这些题,规则如下:

  • 每一回合,你和 Tourist 分别选出一道题,选择时你们互不了解对方的选项;
  • 如果你选的题和 Tourist 一样,那么就删除这道题,如果 道题已经删光那么游戏结束并且你获得零分,否则进入下一回合;
  • 如果你选的题和 Tourist 不一样,那么游戏结束,你的得分是你这回合选的这道题的得分。

Tourist 想要最小化你的得分,请问 Tourist 在最优策略下,你期望最多能获得多少分,以浮点数形式输出。

ix35 的注解:Tourist 的决策是基于概率的,也就是可以以一定权重随机选择某道题,而他的目标是使得你在这种情况下任意选择一道题,最大的期望得分最小。

首先肯定状压,设 表示当前局面为集合 的最大期望收益。

中第 道题分数为 ,去除第 题后,你在剩下的题中期望收益为 ,Tourist 的策略是以 概率选择第 道题,那么 就是 考虑二分答案,设当前二分的值为 ,写出答案 的条件,即存在一组 满足: ,当 时,,当 时, 先不管。

上述式子启示我们将 的限制分开讨论。

对于 的,可以直接由条件 得到 ,此时 ,对于 的,直接取 ,显然此时的 都取到下界,这意味着只有此时 ,条件才可能满足。另一方面,因为肯定有一个 (考虑 最大的即可),所以对于 小于 的,剩下的那部分可以直接加到 上。

所以条件可以改写为: 如果真进行二分可以做到 。过不去。

因为满足这个条件说明答案 ,所以最小的满足该条件的 就是答案的最大值,这类似强对偶定理。条件 非常简单,给定了一个 的下界,而条件 中的 其实是个关于 的分段一次函数,且每段斜率都为负。所以我们可以对每段求出函数值等于 的位置,这也给 提供了一个下界。

这些下界都可以 计算得到。故总时间复杂度为 。可以通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;
#define ppc __builtin_popcount
const int N = 25,Sz = 1 << 22;
typedef double db;
int n,a[N];
db f[Sz],b[N];
inline db Solve(int S) {
db lim = 0;
vector<int> id2;id2.reserve(n);
for(int i = 0;i < n;i++) if((S >> i) & 1) {
if(b[i] >= a[i]) lim = max<db>(lim,a[i]);
else id2.push_back(i);
}
db sk = 0,sb = 0;
for(int t = (int)id2.size() - 1;t >= 0;t--) {
db L = !t ? 0 : a[id2[t - 1]],R = a[id2[t]];
sk += 1.0 / (b[id2[t]] - a[id2[t]]);
sb += (-a[id2[t]]) / (b[id2[t]] - a[id2[t]]);

db x = (1 - sb) / sk;
x = min(x,R);
if(x >= L) lim = max(lim,x);
}
return lim;
}
int main() {
cin >> n;
for(int i = 0;i < n;i++) cin >> a[i];
sort(a,a + n);
for(int S = 0;S < (1 << n);++S) {
if(ppc(S) <= 1) continue;
for(int i = 0;i < n;i++)
if((S >> i) & 1)
b[i] = f[S - (1 << i)];
f[S] = Solve(S);
}
printf("%.12lf\n",f[(1 << n) - 1]);
return 0;
}