Problem
个人进行 轮淘汰赛。已知 战胜 胜率为 ,求一个排列使得 获胜概率最大.
Solution
首先考虑一个暴力做法:如果已经知道了排列,怎么计算 获胜的概率。
我们令 表示在进行到第 轮时, 为擂主的概率。
那么如果知道了排列顺序 ,则可以直接按照下面这个方式进行DP:
f[1][d[1]] = 1;
F(i, 2, n)
F(j, 1, n)
if (d[i] ^ j) {
f[i][d[i]] += f[i - 1][j] * a[d[i]][j];
f[i][j] = f[i - 1][j] * a[j][d[i]];
}
这样子会发现 时刚好被卡,然后这时有一个显然的结论:
1如果想要赢得概率最大,必须要最后一个出场
证明是几乎显然的,我们可以很容易的通过调整法反证。
于是 搜索 + 简单的DP 可以得到30分的不错分数。
并且通过这个性质,我们容易想到一个贪心的做法:
通过赢 概率越大的放的越前,并且在此之后对排列进行调整,具体是每次枚举两个人,看如果交换它们后, 赢得概率的是否变大,如果变大就进行调整。这样进行若干次之后,必定某一时刻会达到最优排列。
这种 调整 + 贪心 的思想可以拿到100分的不错分数,并且时间复杂度十分优秀。
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define mem(a, b) memset(a, b, sizeof a)
const int N = 20;
using namespace std;
int n, d[N]; bool vis[N];
double a[N][N], Ans, f[N][N];
double Doit() {
mem(f, 0), f[1][d[1]] = 1;
F(i, 2, n)
F(j, 1, n)
if (d[i] ^ j) {
f[i][d[i]] += f[i - 1][j] * a[d[i]][j];
f[i][j] = f[i - 1][j] * a[j][d[i]];
}
return f[n][1];
}
int main() {
scanf("%d", &n);
F(i, 1, n)
F(j, 1, n)
scanf("%lf", &a[i][j]);
F(i, 1, n - 1)
d[i] = i + 1;
d[n] = 1;
F(k, 1, n)
F(i, 1, n - 2)
F(j, i + 1, n - 1) {
Ans = Doit();
swap(d[i], d[j]);
double nw = Doit();
if (nw < Ans)
swap(d[i], d[j]);
}
printf("%.7lf\n", Doit());
}
事实上,这题显然是一道状压DP的题目。
但我们发现无论怎么样状压,正着推需要进行取max和累加两种操作,显然是不行的。
那么尝试着从最终的结果往回推,发现问题就变得简单了许多。
因为任何一个局面(我们用状态 代替),最终的结果一定都是 赢,那不妨枚举一下当前这个局面下谁第一个出场,以及第一个出场的人与谁进行pk,并转移到当前状态。
那么我们就让 表示当前局面为 的情况下, 第一个出场, 最后赢的概率。
每次则枚举一个与 进行决斗的人 ,然后通过这样的转移来进行:
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define mx(a, b) ((a) = max(a, b))
#define mem(a, b) memset(a, b, sizeof a)
const int N = 19, M = 262200;
using namespace std;
int n, d[N], shl[N];
double ans, a[N][N], f[M][N];
int main() {
scanf("%d", &n);
F(i, 1, n)
F(j, 1, n)
scanf("%lf", &a[i][j]);
shl[0] = 1;
F(i, 1, n)
shl[i] = shl[i - 1] * 2;
f[1][1] = 1;
F(i, 1, shl[n] - 1) {
mem(d, 0);
for (int x = i; x ; d[++ d[0]] = x & 1, x >>= 1);
F(j, 1, n)
if (d[j])
F(k, 1, n)
if (d[k] && (k ^ j))
mx(f[i][j], f[i - shl[j - 1]][k] * a[k][j] + f[i - shl[k - 1]][j] * a[j][k]);
}
F(i, 1, n)
mx(ans, f[shl[n] - 1][i]);
printf("%.7lf", ans);
}