这是一个懒人的记录,因为要做的题目太多,这篇部分题目就写一下思路并不会有代码
T1
思路
f[i][j]表示A在i,B在j的概率,go[i]表示从i点到相邻某一个点的概率,
go[i]=1−pdu[i]
g
o
[
i
]
=
1
−
p
d
u
[
i
]
那么
f(i)(j)=f(i)(j)pipj+∑f(x)(j)go(x)pj+f(i)(y)go(y)pi+f(x)(y)go(y)go(x)
f
(
i
)
(
j
)
=
f
(
i
)
(
j
)
p
i
p
j
+
∑
f
(
x
)
(
j
)
g
o
(
x
)
p
j
+
f
(
i
)
(
y
)
g
o
(
y
)
p
i
+
f
(
x
)
(
y
)
g
o
(
y
)
g
o
(
x
)
其中x与i相连,y与j相连
然后n*n个方程可以跑高斯消元了
需要注意的是,方程中等式右边在同一个f里的两个点不能相等,因为一旦相等就已经结束,不会再有走到这个点的概率
T2
思路
这样乱走的小数据无向图考虑高斯消元
f[i][j]表示在i点血量还有j的概率,
那么
f(i)(j)=∑f[v(i)][j+val[i]]du[v(i)]
f
(
i
)
(
j
)
=
∑
f
[
v
(
i
)
]
[
j
+
v
a
l
[
i
]
]
d
u
[
v
(
i
)
]
我们考虑根据血量一层一层倒推着解,但是当val[i]=0的时候是同一层的,那么我们可以用高斯消元了
我们建立高斯消元的系数矩阵,可以发现消耗量为0的系数矩阵是不会变的,会变的只有常数项(我们可以通过移项将不同的f[v(i)][j+val(i)]放到常数项里),这样我们不用每次建立初始消耗量为0矩阵,只需要建立一个,然后自己模拟出三角消元的过程,时间复杂度可以达到
O(n2h+2∗m)
O
(
n
2
h
+
2
∗
m
)
最后还是要注意走到n点就不会再走了也不会有转移
数据可能有重边+自环坑死我。
代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=200;
const int M=5005;
const double eps=1e-12;
struct hh{int i;double t;}by[N] [N];
int tot,nxt[M*2],point[N],v[M*2],n,cnt[N],val[N],du[N];double a[N] [N],f[N] [10005],b[N];
void addline(int x,int y){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}
void init()
{
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (fabs(a[j][i])>eps)
{
double t=a[j][i]/a[i][i];
for (int k=i;k<=n;k++) a[j][k]-=t*a[i][k];
by[j][++cnt[j]].i=i; by[j][cnt[j]].t=t;
}
}
void gauss(int hp)
{
for (int i=n;i>=1;i--)
{
f[i][hp]=b[i]/a[i][i];
for (int j=1;j<i;j++) b[j]-=f[i][hp]*a[j][i];
}
}
int main()
{
int m,hp;scanf("%d%d%d",&n,&m,&hp);
for (int i=1;i<=n;i++) scanf("%d",&val[i]);
for (int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
addline(x,y);du[x]++;
if (x!=y) addline(y,x),du[y]++;
}
for (int i=1;i<=n;i++)
{
a[i][i]=1;
if (val[i]) continue;
for (int j=point[i];j;j=nxt[j])
if (v[j]!=n) a[i][v[j]]-=1.0/du[v[j]];
}
init();
for (int i=hp;i>=1;i--)
{
memset(b,0,sizeof(b));
if (i==hp) b[1]=1.0;
for (int j=1;j<=n;j++)
if (val[j] && i+val[j]<=hp)
{
for (int k=point[j];k;k=nxt[k])
if (v[k]!=n) b[j]+=f[v[k]][i+val[j]]/(du[v[k]]*1.0);
}
for (int j=1;j<=n;j++)
for (int k=1;k<=cnt[j];k++) b[j]-=b[by[j][k].i]*by[j][k].t;
gauss(i);
}
double ans=0;
for (int i=1;i<=hp;i++) ans+=f[n][i];
printf("%.8lf",ans);
}
T3
思路
凡凡你个奸商
我们设g(i)表示当前有i种邮票,得到n种的期望步数
那我们推一下吧,假设我们有k种邮票,设s=k/n,即再买一张买到已有的概率是s,那么再买t次可以买到新邮票的概率是s^(t-1)*(1-s)
我们现在的目的是求一下平均拿多少次能拿到一个新邮票
那么期望
=(1−s)+2s(1−s)+3s2(1−s)+...=(1−s)∗(1+2s+3s2+...)=(1−s)E=E−sE
=
(
1
−
s
)
+
2
s
(
1
−
s
)
+
3
s
2
(
1
−
s
)
+
.
.
.
=
(
1
−
s
)
∗
(
1
+
2
s
+
3
s
2
+
.
.
.
)
=
(
1
−
s
)
E
=
E
−
s
E
E=1+2s+3s2+... E = 1 + 2 s + 3 s 2 + . . .
sE=s+2s2+3s3+... s E = s + 2 s 2 + 3 s 3 + . . .
E−sE=1+s+s2+s3+... E − s E = 1 + s + s 2 + s 3 + . . .
等比数列求和公式?
最后的期望=
1/(1−s)
1
/
(
1
−
s
)
(因为s^INF趋近于0,所以分子为1)
也就是
=n/(n−k)
=
n
/
(
n
−
k
)
,也就是说平均拿
n/(n−k)
n
/
(
n
−
k
)
次就会有一个新的邮票
推完这个我们就可以知道 g(i)=g(i+1)+nn−i g ( i ) = g ( i + 1 ) + n n − i , g(n)=0 g ( n ) = 0
然后我们定义pr(x,i)表示买x次能从i种买到n种的概率,实际上pr(x,i)就是再买t次可以买到新邮票的概率是s^(t-1)*(1-s)
则有
g(i)=∑INFx=1x∗pr(x,i)
g
(
i
)
=
∑
x
=
1
I
N
F
x
∗
p
r
(
x
,
i
)
然后可以步入正题啦(?!)
设计状态f[i][j]表示现在有i种邮票,下一次购买需要j元,得到n种邮票的期望花费
有i/n的概率,转移到f[i][j+1],花费是j
有(n-i)/n的概率,转移到f[i+1][j+1],花费是j
那么我们可以得到状态转移
f[i][j]=j+f[i][j+1]∗i/n+f[i+1][j+1]∗(n−i)/n
f
[
i
]
[
j
]
=
j
+
f
[
i
]
[
j
+
1
]
∗
i
/
n
+
f
[
i
+
1
]
[
j
+
1
]
∗
(
n
−
i
)
/
n
但是j是INF啊,所以这个递推无法进行,考虑f[i][j]和f[i][j+1]的关系
并且 f[i][j+1]−f[i][j]=∑x=1INFx∗pr(x,i)=g(i) f [ i ] [ j + 1 ] − f [ i ] [ j ] = ∑ x = 1 I N F x ∗ p r ( x , i ) = g ( i )
那么就可以进一步优化我们的状态转移
然后移项得
我们要求的是f[0][1],所以对于j!=1的情况忽略,定义F[i]表示f[i][1],也就是现在有i种邮票要得到n种的期望花费
柿子又可以简化啦!
至此解出F[i]和g[i]就可以递推求解了。
#include <cstdio>
using namespace std;
const int N=10005;
double f[N],g[N];
int main()
{
int n;scanf("%d",&n);
for (int i=n-1;i>=0;i--)
g[i]=g[i+1]+n/(double)(n-i),f[i]=((f[i+1]+g[i+1])*(n-i)+g[i]*i+n)/(double)(n-i);
printf("%.2lf",f[0]);
}
真·推柿两小时码码1分钟
T4
[BZOJ2707][SDOI2012]走迷宫(概率dp+高斯消元)
思路
首先这道题目所说的到不了终点不仅仅指起点终点不连通,还包括如果这条路有走到某个节点的期望并且这个节点到不了终点,那么这也是到不了的情况
转换为代码就是先tarjan缩点,然后对于出度为0的点如果S可以到达并且不是T那就GG
然后就是平常的E[i]表示i到达T的期望步数,E[T]=0
E[i]=∑E[v(i)]/du(i)+1
E
[
i
]
=
∑
E
[
v
(
i
)
]
/
d
u
(
i
)
+
1
然后对于每一个强联通分量里的点根据拓扑倒序求出E[i]求解。
强联通分量的点与点之间再求E[i]
T5
[BZOJ2688]Green Hackenbush(博弈+概率dp)
思路
树上删边博弈:每个节点的异或值是所有的(子节点的异或值+1)的异或和;对于一棵树来说就是根节点的异或值;对于n棵树来说就是所有树的异或值,是0就先手败,1就先手胜
我们考虑一棵n个节点的二叉树有多少种形态(卡特兰数)
h[n]=∑n−1i=0h[i]∗h[n−i−1]
h
[
n
]
=
∑
i
=
0
n
−
1
h
[
i
]
∗
h
[
n
−
i
−
1
]
g[i][j]表示i个节点的二叉树异或值为j的概率
我们还是通过判断左右子树来解出
g[n][(x+1)∧(y+1)]=∑n−1i=0g[h[i]][x]∗g[h[n−i−1]][y]
g
[
n
]
[
(
x
+
1
)
∧
(
y
+
1
)
]
=
∑
i
=
0
n
−
1
g
[
h
[
i
]
]
[
x
]
∗
g
[
h
[
n
−
i
−
1
]
]
[
y
]
f[i][j]表示前i个子树异或值为j的概率,那么
f[i][j∧k]=f[i−1][j]∗g[a[i]][k]
f
[
i
]
[
j
∧
k
]
=
f
[
i
−
1
]
[
j
]
∗
g
[
a
[
i
]
]
[
k
]
我们最后要的答案是1-f[n][0]
T6
[POJ2096]Collecting Bugs(期望dp)
题意
一个软件有s个子系统,会产生n种bug,某人一天发现一个bug,这个bug属于一个子系统,属于一个分类。每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n
问发现n种bug,每个子系统都发现bug的天数的期望。
思路
f[i][j]表示发现了i种bug分布在j个系统的期望天数,我们的答案就是f[n][s]
这玩意很明显可以从四个状态转移过来,我们分类讨论
f[i][j]+=(f[i−1][j]+1)∗n−in∗js
f
[
i
]
[
j
]
+
=
(
f
[
i
−
1
]
[
j
]
+
1
)
∗
n
−
i
n
∗
j
s
好吧知道这样讨论就可以了,博主太懒不想写剩下的三种了
T7
[POJ2125]Check the difficulty of problems(概率dp)
题意
t支队伍,m个问题,要求每个队伍至少回答对1个问题,至少有一支队伍回答出n个或以上个问题。求事件发生的概率
思路
我们对每支队伍进行dp,f[i][j]表示现在i个问题答对了j道的概率,那么(假设答对问题的概率是p)
f[i][j]=f[i-1][j]*(1-p)+f[i-1][j-1]*p
那么我们在计算的时候用总的概率减去不合法的概率得到答案
这里总的概率指的是所有队伍答对非0问题个数的概率(1-f[m][0]),然后减去所有队伍答对[1,n-1]的题目概率
T8
[POJ3744]Scout YYF I(概率dp+矩阵乘法)
题意
在一条布满地雷的路上,你现在的起点在1处。在N个点处布有地雷,1<=N<=10。地雷点的坐标范围:[1,1e8]
每次有p的概率前进一步,1-p的概率前进2步。问顺利通过这条路的概率。就是不要走到有地雷的地方。
思路
很简单的设计出状态f[i]表示到i号点还活着的概率(有点阴森森的)
那么对于i这个点不是地雷,f[i]=f[i-1]* p+f[i-2]*(1-p)
i要是地雷f[i]=0
但是我们发现这个n很大,显然不能递推,而且我们发现这个转移的状态还是固定的,那么矩阵乘法走起
那么还有一个问题,这个路长到无限,怎么定义最后是安全的概率呢?
我们可以用1-死去的概率就是活着的概率啦,死去的概率是什么呢?是对于每颗地雷踩中他的概率。踩中一颗就GG,所以活着的概率要乘起来
为什么可以划分成n段去求呢?因为最后活着的概率是相乘的!即走到这一步的时候默认的活着的概率是1
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct mat
{
double sq[5][5];
void clear(){memset(sq,0,sizeof(sq));}
}f,mul;
int a[20];double p;
mat cf(mat a,mat b)
{
mat c;c.clear();
for (int i=1;i<=2;i++)
for (int j=1;j<=2;j++)
for (int k=1;k<=2;k++)
c.sq[i][j]+=a.sq[i][k]*b.sq[k][j];
return c;
}
double ksm(int k)
{
mul.sq[1][1]=p; mul.sq[1][2]=1-p;
mul.sq[2][1]=1; mul.sq[2][2]=0;
f.sq[1][1]=1; f.sq[2][1]=0;
for (;k>0;k>>=1,mul=cf(mul,mul))
if (k&1) f=cf(mul,f);
return f.sq[1][1];
}
int main()
{
int n;
while (scanf("%d%lf",&n,&p)!=EOF)
{
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
int last=1,now; double ans=1;
for (int i=1;i<=n;i++) ans*=(1-ksm(a[i]-a[i-1]-1));
printf("%.7lf\n",ans);
}
}
T9
[codeforces 148D]Bag of mice(概率dp)
题意
有两个人从一个袋子里抓老鼠,w只白老鼠,b只黑老鼠,每次每人等概率的抓出一只老鼠,第二个人抓完后会等概率的跳出一只老鼠,第一个抓到白老鼠的人赢,求第一个人赢得概率。
思路
设f[i][j]表示还有i只黑老鼠,j只白老鼠的时候第一个人(A)win的概率
那么有如下情况
A选白老鼠,直接胜利,概率是
i/(i+j)
i
/
(
i
+
j
)
A选黑老鼠,B选白老鼠,获胜概率是0不用考虑
A选黑老鼠,B选黑老鼠,跳出一只白老鼠,获胜概率是
j(i+j)∗(j−1)(i+j−1)∗i(i+j−2)∗f[i−2][j−1]
j
(
i
+
j
)
∗
(
j
−
1
)
(
i
+
j
−
1
)
∗
i
(
i
+
j
−
2
)
∗
f
[
i
−
2
]
[
j
−
1
]
A选黑老鼠,B选黑老鼠,跳出一只黑老鼠,获胜概率是
j(i+j)∗(j−1)(i+j−1)∗j−2(i+j−2)∗f[i−3][j]
j
(
i
+
j
)
∗
(
j
−
1
)
(
i
+
j
−
1
)
∗
j
−
2
(
i
+
j
−
2
)
∗
f
[
i
−
3
]
[
j
]
初始化就是如果全是黑老鼠获胜率为0,全是白老鼠获胜率为1
T10
[ZOJ3329]One Person Game(期望dp+迭代)
题意
有3个筛子,分别有k1,k2,k3个面。每次掷筛子,如果三个面为指定的a,b,c,则分数置0,否则加上三个筛子的和。当总分大于等于n结束。求游戏的期望步数
思路
比较经典的题目,设f[i]表示分数为i到游戏结束的期望步数,那么f[0]就是答案
f[i]=∑k1+k2+k3x=1f[i+x]∗p[x]+f[0]∗p[0]+1
f
[
i
]
=
∑
x
=
1
k
1
+
k
2
+
k
3
f
[
i
+
x
]
∗
p
[
x
]
+
f
[
0
]
∗
p
[
0
]
+
1
,这里p[i]表示投出i这个总和的概率,p[0]就是投出特殊情况的概率
我们可以发现每一个都含有f[0]啊,怎么求呢?高斯消元?不行太麻烦了,我们考虑用迭代
设 f[i]=a[i]∗f[0]+b[i] f [ i ] = a [ i ] ∗ f [ 0 ] + b [ i ]
则 f[i]=∑[(a[i+k]∗p[k]+p[0])∗E[0]+b[i+k]∗p[k]]+1 f [ i ] = ∑ [ ( a [ i + k ] ∗ p [ k ] + p [ 0 ] ) ∗ E [ 0 ] + b [ i + k ] ∗ p [ k ] ] + 1
则 a[i]=a[i+k]∗p[k]+p[0] a [ i ] = a [ i + k ] ∗ p [ k ] + p [ 0 ] , b[i]=b[i+k]∗p[k]+1 b [ i ] = b [ i + k ] ∗ p [ k ] + 1
那么最后的答案 f[0]=b[0]/(1−a[0]) f [ 0 ] = b [ 0 ] / ( 1 − a [ 0 ] )
代码
#include <cstdio>
#include <cstring>
using namespace std;
const int N=505;
double p[N],pa[N],pb[N];
int main()
{
int T,n,k1,k2,k3, a, b, c;
scanf("%d",&T);
while (T--)
{
scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
memset(p,0,sizeof(p));
double one=1.0/(k1*k2*k3);
for (int i=1;i<=k1;i++)
for (int j=1;j<=k2;j++)
for (int k=1;k<=k3;k++)
if (i!=a || j!=b || k!=c) p[i+j+k]+=one;
memset(pa,0,sizeof(pa));
memset(pb,0,sizeof(pb));
for (int i=n;i>=0;i--)
{
for (int j=3;j<=k1+k2+k3 && (i+j)<=n;j++)
pa[i]+=pa[i+j]*p[j],pb[i]+=pb[i+j]*p[j];
pa[i]+=one; pb[i]++;
}
printf("%.15lf\n",pb[0]/(1.0-pa[0]));
}
}
T11
题意
一棵树,一个人初始在1号点。每次到达一个点,有ki的概率被杀死,并且回到1号点,有ei的概率直接逃离,然后等概率的逃到与他相邻的节点(1-ki-ei)/du[i],每次移动步数+1。 求逃出去的期望步数。
思路
和刚才那道题目很像啊,但是这个逃离的概念比较模糊,我们就设一个0点表示
依然设E[i]表示在i点逃出去的期望步数,显然E[0]=0
则
E[i]=∑(E[v(i)]+1)∗1−k−edu[i]+E[1]∗k+E[0]∗e=∑(E[v(i)]+1)∗1−k−edu[i]+E[1]∗k
E
[
i
]
=
∑
(
E
[
v
(
i
)
]
+
1
)
∗
1
−
k
−
e
d
u
[
i
]
+
E
[
1
]
∗
k
+
E
[
0
]
∗
e
=
∑
(
E
[
v
(
i
)
]
+
1
)
∗
1
−
k
−
e
d
u
[
i
]
+
E
[
1
]
∗
k
而且这是一棵树,我们要利用好这个条件,我们希望通过树形dp的方式递推
为了方便我们设
p[i]=1−ki−eidu[i]
p
[
i
]
=
1
−
k
i
−
e
i
d
u
[
i
]
那么柿子要微微改一下:
E[i]=∑((E[son(i)]+1)∗p[i])+(E[fa]+1)∗p[i]+E[1]∗k[i]
E
[
i
]
=
∑
(
(
E
[
s
o
n
(
i
)
]
+
1
)
∗
p
[
i
]
)
+
(
E
[
f
a
]
+
1
)
∗
p
[
i
]
+
E
[
1
]
∗
k
[
i
]
如果考虑用父亲更新儿子的话,E[fa[i]]和E[1]是未知的,而E[son[i]]是已知的,我们把ta当做一个常数,根据上一个题目的经验画一下柿子
E[i]=A[i]∗E[fa]+B[i]∗E[1]+C[i]
E
[
i
]
=
A
[
i
]
∗
E
[
f
a
]
+
B
[
i
]
∗
E
[
1
]
+
C
[
i
]
然后学着上一个题目带进去,可以得到
那么我们设
T[i]=1−∑A[son(i)]∗p[i]
T
[
i
]
=
1
−
∑
A
[
s
o
n
(
i
)
]
∗
p
[
i
]
,然后可以得到
A[i]=p[i]T[i]
A
[
i
]
=
p
[
i
]
T
[
i
]
,
B[i]=k[i]+∑B[son(i)]∗p[i]T[i]
B
[
i
]
=
k
[
i
]
+
∑
B
[
s
o
n
(
i
)
]
∗
p
[
i
]
T
[
i
]
,
C[i]=∑C[son(i)]∗p[i]+du[i]∗p[i]T[i]
C
[
i
]
=
∑
C
[
s
o
n
(
i
)
]
∗
p
[
i
]
+
d
u
[
i
]
∗
p
[
i
]
T
[
i
]
,到这里一遍树形dp可以求出A[1],B[1],C[1]
我们要的答案是E[1],而1节点没有父亲,所以
E[1]=B[1]∗E[1]+C[1]
E
[
1
]
=
B
[
1
]
∗
E
[
1
]
+
C
[
1
]
,
E[1]=C[1]1−B[1]
E
[
1
]
=
C
[
1
]
1
−
B
[
1
]
分母为0则impossible
初始化的话,对于一个叶子结点来说
E[i]=(E[fa]+1)∗p[i]+E[1]∗k[i]
E
[
i
]
=
(
E
[
f
a
]
+
1
)
∗
p
[
i
]
+
E
[
1
]
∗
k
[
i
]
,则
A[i]=p[i]
A
[
i
]
=
p
[
i
]
,
B[i]=k[i]
B
[
i
]
=
k
[
i
]
,
C[i]=p[i]
C
[
i
]
=
p
[
i
]
T10&11小结
我们对于这种数据范围不适合高斯消元却用到了前面的状态的柿子,可以考虑用迭代解决,把柿子转化为a*f[前状态1]+b*f[前状态2]+常数这种样子,再代入原柿子的[可递推项]运用系数的转化去完成
T12
[codefoces453A]Little Pony and Expected Maximum(概率期望dp)
思路
f[i]表示i为最大值的概率,那么ans+=f[i]*i
f[i]=(im)n−(i−1m)n
f
[
i
]
=
(
i
m
)
n
−
(
i
−
1
m
)
n
表示这n种都选择1-i的数字减去都选择1-i-1之内的数字,这种容斥的思想是很常见的
T13
[codefoces498B]Name That Tune(概率dp)
题意
有n首歌,顺序播放。只有第i首歌被辨认出,才能播放第i+1首歌。对于每首歌有两个值pi,ti。表示在播放1..ti-1的时间内有pi/100的概率猜出这首歌,如果播放了ti分钟,那么一定可以辨认出。问播放时间为T,辨认出歌的数量的期望。
思路
f[i][j]
f
[
i
]
[
j
]
表示第j秒该听第i首歌的概率,即已经听出第i-1首歌的概率。
f[i][j+1]+=f[i][j]∗(1−p[i])
f
[
i
]
[
j
+
1
]
+
=
f
[
i
]
[
j
]
∗
(
1
−
p
[
i
]
)
f[i+1][j+1]+=f[i][j]∗p[i]
f
[
i
+
1
]
[
j
+
1
]
+
=
f
[
i
]
[
j
]
∗
p
[
i
]
这样子有一种情况是错误的,就是
f[i][j]
f
[
i
]
[
j
]
经过
t[i]
t
[
i
]
的时间会转移到
f[i][j+t[i]]
f
[
i
]
[
j
+
t
[
i
]
]
但是实际上应该转移到的是
f[i+1][j+t[i]]
f
[
i
+
1
]
[
j
+
t
[
i
]
]
那么这种情况特殊算一下就可以了。
ans=∑n+1i=2f[i][m]∗(i−1)
a
n
s
=
∑
i
=
2
n
+
1
f
[
i
]
[
m
]
∗
(
i
−
1
)
T14
题意
有一个大小为k的缓存区,每次从n种物品中按照一定的概率选取一种物品尝试放进去.同一个物品每一次选取的概率都是相同的.如果这种物品已经放进去过就不再放进去.如果缓存区满了就把放进去的时间离现在最远的物品拿出来.问10^100次后每个物品在缓冲区中的概率
思路
如果是正着考虑,那么我们会出现很多废弃的状态。而且还会出现进去的被弹出来的情况,十分不好想。那么我们倒着考虑,那么最后一次加入的一定在最终的集合中。因为10^100基本上接近无限了,那么最后的集合一定是满的。
也就是一定存在k个不同的物品。
dp[i]表示到达状态i的概率。
dp[i|(1<<j)]+=dp[i]∗p[j] d p [ i | ( 1 << j ) ] + = d p [ i ] ∗ p [ j ] ,j不在i中。注意的是每次选择还有一定的概率选择到i集合中的数。所以dp[i]/=(1-sum) ,sum表示i集合中数的概率。