其他分享
首页 > 其他分享> > 玄学の题解(概率与期望题解)

玄学の题解(概率与期望题解)

作者:互联网

T1 玄学のRed is good

题目描述


桌面上有R张红牌和B张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到1美元,黑牌则付出1美元。可以随时停止翻牌,在最优策略下平均能得到多少钱。

输入格式


一行输入两个数R,B,其值在0到5000之间

输出格式


在最优策略下平均能得到多少钱。

样例输入

5 1

样例输出

4.166666

水の闲话


说句实话,这道题的确是我如果不看题结果不掉的题。。。怎么说呢,因为刚开始听课的时候并没有清晰的理解期望这个玄学知识点是什么意思

(简单来说,就是我是蒟蒻(汗))

搭嘎(But,但是),通过看这道题的题解以及询问身边的巨巨zxs,吴来娃(我)对期望这个概念的理解加深了不少,那么接下来,我们进入正题。。。

Solution


刚开始一直想开一个一维数组表示获得价值为i下的期望值,不过这样的话红黑期望值不好表示。

注意到告诉了我们红色和黑色的卡牌张数及范围,需要 简单地 动脑子努力想到开一个二维数组,表示在抽取了i张红牌和j张黑牌时的期望。

这样的话,用f[i-1][j]表示上一个状态,则f[i][j]表示抽出一张红牌,黑牌同理。

于是,抽出一张红牌获得一张小钱钱(语出蟹老板)期望值加1,即f[i-1][j]+1; 抽出一张黑牌则失去一张小钱钱,f[i][j-1]-1。

然而这只是个数,想得到期望还需要乘一个概率,这个好表示,即i/(i+j)和j/(i+j)

最终,得出状态转移方程:

f[i][j]=max(0,(f[i-1][j]+1)×(i/(i+j))+(f[i][j-1]-1)×(j/(i+j)))

然而注意的不应当只有这些,在循环嵌套过程中,还需要注意为每一层更新的f[i][0]赋一个初始期望值,即i,表示抽出i个红牌的期望值为i,这样就可以顺利ac了。

注意:


1.如果内存限制小的话,需要用滚动数组优化空间直接从196204 KiB变为432 KiB,非常方便的哟!!

2.题目是不许四舍五入的,这样的话需要手动模拟一个取数,不过较为简单,不再说明原理喽略略略!!

接下来看马の码:

 1 #include<bits/stdc++.h>
 2 #define wsn double
 3 using namespace std;
 4 const int NN=5005;
 5 int R,B;
 6 wsn ans,f[2][NN];
 7 namespace WSN{
 8     inline int main(){
 9         cin>>R>>B;
10         f[1][0]=1.0;
11         f[0][1]=0.0;
12         for(int i=1;i<=R;i++,f[i&1][0]=i)
13             for(int j=1;j<=B;j++)
14                 f[i&1][j]=max(0.0,(f[(i-1)&1][j]+1.0)*((wsn)i/(i+j))+(f[i&1][j-1]-1.0)*((wsn)j/(i+j)));
15         f[R&1][B]=((int)(f[R&1][B]*1e6))/1e6;
16         printf("%.6lf",f[R&1][B]);
17         return 0;
18     }
19 }
20 signed main(){return WSN::main();}
View Code

其实还有一个高人一等的坐在我旁边的旁边的JYFHYX绞尽脑汁,搜肠刮肚,冥思苦想出的正解爆搜超神代码,虽然只有60分。。不过值得蒟蒻的小马借鉴啦!!

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 double r,b;
 4 double f[5001][5001];
 5 inline double dp(double black,double red)
 6 {
 7     int j=black,b=red;
 8     if(f[j][b])
 9     return f[j][b];
10     if(black+red==0)
11     return 0;
12     return f[j][b]=(red/(black+red))*(max(dp(black,red-1.0),0.0)+1)+(black/(black+red))*(max(dp(black-1.0,red),0.0)-1);
13 }
14 int main()
15 {
16     cin>>r>>b;
17     int n=r+b;
18     for(int i=1;i<=r;i++)
19     {
20         f[0][i]=i;
21     }
22     for(int i=1;i<=b;i++)
23     f[i][0]=-i;
24     double ans;
25     ans=dp(b,r);
26     ans=((int)(ans*1e6))/1e6;
27     printf("%.6lf",ans);
28 }
View Code

T2 聪聪和可可

传送门

水の闲话


这题因为放在专题里比较靠前我以为是一个靠推一些概率方程出来的题,没咋往模拟那里想。于是便和坐我旁边的巨巨zxs考虑出了一种猫不动的算法,成功的过掉了样例(两个),然而10fen就离谱(A1,T5,W4)在此粘贴一下以便叙述故事:

 1 #include<bits/stdc++.h>
 2 #define db double
 3 using namespace std;
 4 const int NN=1005;
 5 int N,E,C,M,level,cnt,out[NN];
 6 db f[NN],dp,deg[NN];
 7 struct SNOW{
 8     int to,next;
 9 }e[NN<<1]; int tot,r[NN<<1];
10 struct wsn{
11     int q,data;
12     friend bool operator<(wsn a,wsn b){return a.data>b.data;}
13 }; wsn cc; priority_queue<wsn> Q;
14 bool v[NN]; int dis[NN];
15 inline void add(int x,int y){
16     e[++tot]=(SNOW){y,r[x]};
17     r[x]=tot; out[x]++;
18 }
19 inline void snow(int st){
20     int x,y;
21     cc.q=st; cc.data=0;
22     Q.push(cc);
23     memset(v,false,sizeof(v));
24     dis[st]=0;
25     while(!Q.empty()){
26         x=Q.top().q; y=Q.top().data;
27         Q.pop();
28         if(!v[x]){
29             v[x]=true;
30             for(int i=r[x];i;i=e[i].next){
31                 cc.q=e[i].to;
32                 if(dis[cc.q]>y+1){
33                     dis[cc.q]=y+1;
34                     cc.data=dis[cc.q];
35                     Q.push(cc);
36                 }
37             }
38         }
39     }
40 }
41 inline void Search(int x){
42     level++;
43     if(level>=dis[x]){
44         dp+=dis[x]*deg[x];
45         level--;
46         return;
47     }
48     for(int i=1;i<=N;i++) deg[i]*=f[x];
49     Search(x);
50     for(int i=r[x];i;i=e[i].next) Search(e[i].to);
51     level--;
52 }
53 inline int read(){
54     int x=0,f=1; char ch=getchar();
55     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
56     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
57     return x*f;
58 }
59 namespace WSN{
60     inline int main(){
61         memset(dis,50,sizeof(dis));
62         N=read(),E=read();
63         C=read(),M=read();
64         for(int i=1;i<=E;i++){
65             int x=read(),y=read();
66             add(x,y); add(y,x);
67         }
68         snow(C);
69         for(int i=1;i<=N;i++) f[i]=1.0/(out[i]+1),dis[i]=ceil(dis[i]/2.0),deg[i]=1.0;
70         Search(M);
71         printf("%.3lf",dp);
72         return 0;
73     }
74 }
75 signed main(){return WSN::main();}
View Code

后来zxs画出了美妙的反驳图:反驳图 如果猫在顶点,老鼠在左端点。老鼠第一次向右走,如果猫不动,只找距离足够,则猫会在老鼠运动到中点时一举干掉它。

也就是说如上代码会使猫像预言家一样预先知道老鼠的位置并找到最优路线吃掉它,然而。。。

猫是向着老鼠所在的当前点按最短路去的,这样的话猫会走上最左边的边,而他不能换边,而老鼠可能会向右走,这样而这会走出一个近似L型路径,所以,需要每次找到猫的两步走到了哪一个点,这便是dji的作用。

于是,发现这是道模拟题。。。

不得不%一下我旁边的旁边的模拟强者JYFHYX小哥哥,在我和zxs热火朝天的打深搜和广搜时,酷酷的打出了正解由于是模拟,思路不大难,于是直接粘贴代码了。

 1 #include<bits/stdc++.h>
 2 #define db double
 3 using namespace std;
 4 const int NN=1005;
 5 int N,E,C,M,level,cnt,out[NN];
 6 db f[NN],dp[NN][NN];
 7 struct SNOW{
 8     int to,next;
 9 }e[NN<<1]; int tot,r[NN<<1];
10 struct wsn{
11     int q,data;
12     friend bool operator<(wsn a,wsn b){return a.data>b.data;}
13 }; wsn cc; priority_queue<wsn> Q;
14 bool v[NN]; int dis[NN],way[NN][NN];
15 inline void add(int x,int y){
16     e[++tot]=(SNOW){y,r[x]};
17     r[x]=tot; out[x]++;
18 }
19 inline void snow(int st){
20     int x,y;
21     cc.q=st; cc.data=0;
22     Q.push(cc);
23     memset(dis,50,sizeof(dis));
24     memset(v,false,sizeof(v));
25     dis[st]=0;
26     while(!Q.empty()){
27         x=Q.top().q; y=Q.top().data;
28         Q.pop();
29         if(!v[x]){
30             v[x]=true;
31             for(int i=r[x];i;i=e[i].next)
32             {
33                 cc.q=e[i].to;
34                 if(dis[cc.q]>y+1){
35                     dis[cc.q]=y+1;
36                     cc.data=dis[cc.q];
37                     Q.push(cc);
38                 }
39             }
40         }
41     }
42     for(int i=1;i<=N;i++)
43         if(i!=st){
44             int minn=0x7fffffff;
45             for(int j=r[i];j;j=e[j].next){
46                 int v=e[j].to;
47                 if(minn>dis[v])
48                     minn=dis[v],way[i][st]=v;
49                 else if(minn==dis[v] && v<way[i][st])
50                     way[i][st]=v;
51             }
52         }
53 }
54 inline db dfs(int cat,int mou){
55     int st_one=way[cat][mou];
56     int st_two=way[st_one][mou];
57     if(dp[cat][mou]) return dp[cat][mou];
58     if(cat==mou) return 0.0;
59     if(st_one==mou||st_two==mou) return 1.0;
60     for(int i=r[mou];i;i=e[i].next)
61         dp[cat][mou]+=(dfs(st_two,e[i].to)+1.0)*f[mou];
62     dp[cat][mou]+=(dfs(st_two,mou)+1.0)*f[mou];
63     return dp[cat][mou];
64 }
65 inline int read(){
66     int x=0,f=1; char ch=getchar();
67     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
68     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
69     return x*f;
70 }
71 namespace WSN{
72     inline int main(){
73         N=read(),E=read();
74         C=read(),M=read();
75         for(int i=1;i<=E;i++){
76             int x=read(),y=read();
77             add(x,y); add(y,x);
78         }
79         for(int i=1;i<=N;i++) f[i]=1.0/(out[i]+1);
80         for(int i=1;i<=N;i++) snow(i);
81         printf("%.3lf",dfs(C,M));
82         return 0;
83     }
84 }
85 signed main(){return WSN::main();}
View Code

尚有女朋友的Vani的故事之:

T3 列队春游(数学大礼包)

背景(是重点):


Vani,你还记得我们的小时候吗?”

Vani的妹子温顺地对Vani低语。

“嗯。。。记得啊!”

题目


N个小朋友站一排,有高有低,一个小朋友只能看见在他之前高于或等于他的那个人与自己之间的一段距离

需要输出小朋友视野总和的期望值

输入格式


第一行一个n,有N个小朋友。接下来N行表示每一个小朋友身高。

样例及说明


输入

3

1 2 3

输出

4.33

说明:

S(1,2,3)=1+2+3=6;

S(1,3,2)=1+2+1=4;。。。。。

E=(6+4+5+4+4+3)/6= 13/3;

(没法复制图片是真的难,不过手打一些甜言蜜语也很爽嘿嘿嘿)

Solution

很惊讶吧,这次没有闲话了哈哈哈(假正经)。那么让我们直接进入正题。。。

这是O(N)算法。

要推导一个数学公式

设P()表示概率。

不低于第i个小朋友身高的小朋友个数(不包括自己)为k,L表示其视野距离。

∑x i=1表示从1连加到x。

A(m,n)包括C,表示n里选择m个进行排列组合

ans=∑n i=1, i∗P(L=i);

因为枚举的每一个P(L>=i)可以转化为P(L=i)一直加到P(L=i+x)。。例如:

E[1]=1P(L=i),E[2]=2P(L=i+1);

ans=E[1]+E[2]=1P(L=i)+2P(L=i+1)=[P(L=i)+P(L=i+1)]+P(L=i+1)=

ans=∑n i=1,P(L>=i);

这就完成了一部重要的化简。。。。

接下来,我么考虑用组合数学。。。。

ans=∑n i=1,1/(A(i-1,n-(k+1))×A(i-1,n));

(此步骤不解释了,比较好懂)

接下来就是一波比较理解起来easy但实际操作极为困难的化简。

过程我就粘一下博客园里大佬的题解仅供参考了(希望这不叫侵权,因为这个博客没有数学符号,太难打)

网上大佬の传送门

接下来化简得到一个

ans=(n+1)/(k+2)(大佬的最后写反了,不过不影响成为大佬)

就可以愉快的打代码了

马の码(这次真的独有,因为没有直接飙到1000)

我是直接比较优先级,这样大大减小了循环的长度

 1 #include<bits/stdc++.h>
 2 #define D double
 3 using namespace std;
 4 const int NN=1005;
 5 int n,a[NN],h[NN],sum[NN];
 6 D ans;
 7 inline int read(){
 8     int x=0,f=1; char ch=getchar();
 9     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
10     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
11     return x*f;
12 }
13 namespace WSN{
14     inline int main(){
15         n=read();
16         for(int i=1;i<=n;i++) a[i]=read();
17         sort(a+1,a+n+1);
18         int bian=0,change=0;
19         for(int i=1;i<=n;i++)
20             if(bian!=a[i]){
21                 change++;
22                 bian=a[i];
23                 h[change]++;
24             }
25             else h[change]++;
26         for(int i=1;i<=change;i++) sum[i]=sum[i-1]+h[i];
27         for(int i=1;i<=change;i++)
28             ans+=1.0*h[i]*(n+1)/(n-sum[i-1]+1);
29         printf("%.2lf",ans);
30         return 0;
31     }
32 }
33 signed main(){return WSN::main();}
View Code

黑化后的Vani的故事之:

T4 守卫者的挑战

背景(是重点):


打开了黑魔法师Vani!!的大门,队员们在迷宫般的路上漫无目的地搜寻着关押applepi的监狱的所在地。突然,眼前一道亮光闪过。“我,Nizem,是黑魔法圣殿的守卫者。如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图……”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为K的包包。

擂台赛一共有N项挑战,各项挑战依次进行。第i项挑战有一个属性ai,如果ai>=0,表示这次挑战成功后可以再获得一个容量为ai的包包;如果ai=-1,则表示这次挑战成功后可以得到一个大小为1 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有必要全部装满,但是队员们必须把 【获得的所有的】地图残片都带走(没有得到的不用考虑,只需要完成所有N项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功L次才能离开擂台。

队员们一筹莫展之时,善良的守卫者Nizem帮忙预估出了每项挑战成功的概率,其中第i项挑战成功的概率为pi%。现在,请你帮忙预测一下,队员们能够带上他们获得的地图残片离开擂台的概率。

输入格式


第一行三个整数N,L,K。

第二行N个实数,第i个实数pi表示第i项挑战成功的百分比。

第三行N个整数,第i个整数ai表示第i项挑战的属性值.

输出格式


一个整数,表示所求概率,四舍五入保留6 位小数。

样例输入1

3 1 0 10 20 30 -1 -1 2

样例输出1

0.300000

Solution

基于对前面的Red is good的理解,此题很容易想到开三维数组来进行状态转移。f[i][j][k]中一维表示打了几关,二维表示赢了几关,三维表示背包容量。

然而我们应注意到,数组中的下标不能为负数,而此题极限的情况为200个k全部是-1,这样的话数组将成为负数下标。所以第三位应开到400,将背包的极限情况考虑到。

dp时分情况考虑,输了的比较简单,赢的情况需要进行判断,最后将所在的范围里的值相加即可。

马の码:

 1 #include<bits/stdc++.h>
 2 #define D double
 3 using namespace std;
 4 const int NN=205;
 5 int N,L,K,a[NN];
 6 D p[NN],dp[NN][NN][NN<<1],ans;
 7 inline int read(){
 8     int x=0,f=1; char ch=getchar();
 9     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
10     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
11     return x*f;
12 }
13 namespace WSN{
14     inline int main(){
15         N=read(),L=read(),K=min(read(),N); D c;
16         for(int i=1;i<=N;i++) scanf("%lf",&c),p[i]=c/100;
17         for(int i=1;i<=N;i++) a[i]=read();
18         dp[0][0][200+K]=1;
19         for(int i=1;i<=N;i++)
20             for(int j=0;j<=i;j++)
21                 for(int k=0;k<=400;k++)
22                 {
23                     dp[i][j][k]+=dp[i-1][j][k]*(1-p[i]);
24                     if(j>0 && k-a[i]>=0) dp[i][j][k]+=dp[i-1][j-1][k-a[i]]*p[i];
25                 }
26         for(int j=L;j<=N;j++)
27             for(int k=200;k<=400;k++)
28                 ans+=dp[N][j][k];
29         printf("%.6lf",ans);
30         return 0;
31     }
32 }
33 signed main(){return WSN::main();}
View Code

VaniのEND一些反思:

从Vani的黑化经历中,我们知道了相伴与爱情的重要,这样的话 ,我们应该珍惜每一次美好的相遇,珍惜每一个爱你的人。。。。

END

标签:ch,期望,NN,玄学,题解,int,dp,cc,dis
来源: https://www.cnblogs.com/hzoi-wsn/p/14890630.html