题目大意:
题目链接:https://jzoj.net/senior/#main/show/6296
第
i
i
i个人有
p
i
p_i
pi的概率会选择1,否则选择0。求在
n
n
n个人中选择
m
m
m个,1和0的个数相等的期望。
思路:
30 p t s 30pts 30pts
O
(
3
n
)
O(3^n)
O(3n)暴力搜索每一个人是不选,选1还是选0。用状压记录每一种选择方案的概率。
空间复杂度
O
(
2
n
)
O(2^n)
O(2n)。
好像
O
(
4
n
)
O(4^n)
O(4n)还可以拿
40
p
t
s
40pts
40pts。
代码
L
i
n
k
Link
Link
100 p t s 100pts 100pts
将
p
i
p_i
pi排序,如果此时最有方案中存在一个选择的人,他左右均有人且都没有被选择,那么固定其他选择的人,在剩余的人中选择,期望一定为一个一次函数。所以这个人肯定王左或往右会更优。
所以设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示在
[
1
,
i
]
[1,i]
[1,i]中选择
j
j
j个人选择1,
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示在
[
i
,
n
]
[i,n]
[i,n]中选择
j
j
j个人选择1。
以
f
f
f为例,如果这一个位置选择0,那么
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
×
(
1
−
p
[
i
]
)
f[i][j]=f[i-1][j-1]\times (1-p[i])
f[i][j]=f[i−1][j−1]×(1−p[i]),如果这一个位置选择1,那么
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
×
p
[
i
]
f[i][j]=f[i-1][j-1]\times p[i]
f[i][j]=f[i−1][j−1]×p[i]。
所以方程就是
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
×
(
1
−
p
[
i
]
)
+
f
[
i
−
1
]
[
j
−
1
]
×
p
[
i
]
f[i][j]=f[i-1][j]\times (1-p[i])+f[i-1][j-1]\times p[i]
f[i][j]=f[i−1][j]×(1−p[i])+f[i−1][j−1]×p[i]
最后枚举前面选多少人,以及前面几个人选1,计算一下答案即可。
时间复杂度
O
(
n
m
)
O(nm)
O(nm)
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=2010;
double p[N],f[N][N],g[N][N],ans,maxn;
int n,m;
int main()
{
freopen("vote.in","r",stdin);
freopen("vote.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lf",&p[i]);
sort(p+1,p+1+n);
f[0][0]=g[n+1][0]=1.0;
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
{
if (!j) f[i][j]=f[i-1][j]*(1-p[i]);
f[i][j]=f[i-1][j]*(1-p[i])+f[i-1][j-1]*p[i];
}
for (int i=n;i>=1;i--)
for (int j=0;j<=m;j++)
{
if (!j) g[i][j]=g[i+1][j]*(1-p[i]);
g[i][j]=g[i+1][j]*(1-p[i])+g[i+1][j-1]*p[i];
}
for (int i=0;i<=m;i++)
{
ans=0.0;
for (int j=0;j<=m/2;j++)
ans+=f[i][j]*g[n-m+i+1][m/2-j];
if (ans>maxn) maxn=ans;
}
printf("%0.8lf",maxn);
return 0;
}