概率DP学习笔记(一)
来源:互联网 发布:管家婆软件客户端 编辑:程序博客网 时间:2024/05/22 16:58
今天学了一下概率DP,做了几个题,记录一下。
HDU4405
这个题目的意思就是,棋盘由0到N构成,每次投骰子能投1-6,如果位置大于等于N,那么就算是到了终点。此外有一些飞行点,到了这个点就会直接到所对应的飞行点,并且可以多次飞行,即如果a是飞到b的飞行点,b又是飞到c的飞行点,那么就可以认为到a点的时候直接飞到了c点。问题是给你一个棋盘大小和飞行点,问你结束游戏所需回合的期望值。
首先这是一个求期望的,所以就用倒推就好,但是记得开始前用dfs处理一下飞行点的问题,因为存在多次飞行的情况不能直接来。
代码如下
#include <bits/stdc++.h>#define N 100100#define INF 0x3f3f3f3f#define LL long long#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double changce[N];map<int,int> mp,fin;void dfs(int beg,int en){ if(mp[en]==0){ fin[beg]=en; }else{ dfs(beg,mp[en]); }}void init(){ for(map<int,int>::iterator a=mp.begin(),b=mp.end();a!=b;++a){ dfs(a->first,a->second); }}int main(){ ios::sync_with_stdio(false); int n,m,temp1,temp2; while(cin>>n>>m&&(n+m)){ mp.clear(); fin.clear(); while(m--){ cin>>temp1>>temp2; mp[temp1]=temp2; } init(); for(int i=1;i<6;++i){ changce[n+i]=0; } changce[n]=0; for(int i=n-1;i>=0;--i){ changce[i]=1; for(int j=1;j<=6;++j){ if(i+j<=n){ if(fin[i+j]==0){ changce[i]+=changce[i+j]/6; }else{ changce[i]+=changce[fin[i+j]]/6; } } } } cout<<fixed<<setprecision(4)<<changce[0]<<endl; } return 0;}
POJ2096
一个软件有s个子系统,会产生n种bug
每天发现一个bug,这个bug属于一个子系统,属于一个分类。
每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n。
问发现n种bug且每个子系统都发现bug所需天数的期望。
是考虑系统、分类数而不考虑具体的是哪种分类,这个问题的抽象就很容易解决了
下面分析出自kuangbin博客
dp[i][j]表示已经找到i种bug,j个系统的bug,达到目标状态的天数的期望
dp[n][s]=0;要求的答案是dp[0][0];
dp[i][j]可以转化成以下四种状态:
dp[i][j],发现一个bug属于已经有的i个分类和j个系统。概率为(i/n)*(j/s);
dp[i][j+1],发现一个bug属于已有的分类,不属于已有的系统.概率为 (i/n)*(1-j/s);
dp[i+1][j],发现一个bug属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(j/s);
dp[i+1][j+1],发现一个bug不属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(1-j/s);
#include <iostream>#include <iomanip>#define N 1010#define INF 0x3f3f3f3f#define LL long long#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double dp[N][N];int main(){ ios::sync_with_stdio(false); int s,n; while(cin>>n>>s){ dp[n][s]=0; for(int i=n;i>=0;--i){ for(int j=s;j>=0;--j){ if(i==n&&j==s){ continue; } dp[i][j]=(i*(s-j)*dp[i][j+1]+ (n-i)*j*dp[i+1][j]+ (n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j); } } /*for(int i=0;i<=s;++i){ for(int j=0;j<=n;++j){ cout<<fixed<<setprecision(4)<<dp[i][j]<<' '; } cout<<endl; }*/ cout<<fixed<<setprecision(4)<<dp[0][0]<<endl; } return 0;}
HDU3853
一个r*c的地图,在每个格子上都有对应的m1、m2、m3,在每一块格子上,下一回合有m1的几率不动,m2的几率下移,m3的几率右移,问你从(1,1)到(r,c)的所需步数期望
这个也很裸,但是有个坑点,见代码
#include <bits/stdc++.h>#define N 1010#define INF 0x3f3f3f3f#define LL long long#define eps 1e-9#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double mp[N][N][3];double dp[N][N];int main(){ //ios::sync_with_stdio(false); int r,c; while(cin>>r>>c){ dp[r-1][c-1]=0; for(int i=0;i<r;++i){ for(int j=0;j<c;++j){ scanf("%lf%lf%lf",mp[i][j],mp[i][j]+1,mp[i][j]+2);//这里使用cin会WA,不明白为什么 } } for(int i=r-1;i>=0;--i){ for(int j=c-1;j>=0;--j){ if(i==r-1&&j==c-1){ continue; }if(fabs(mp[i][j][0]-1.00)<1e-8) continue; dp[i][j]=(dp[i+1][j]*mp[i][j][2]+dp[i][j+1]*mp[i][j][1]+2)/(1-mp[i][j][0]); } } /*for(int i=0;i<r;++i){ for(int j=0;j<c;++j){ cout<<fixed<<setprecision(3)<<dp[i][j]<<' '; } cout<<endl; }*/ cout<<fixed<<setprecision(3)<<dp[0][0]<<endl; } return 0;}
CSU1725
这个题严格说来不是概率DP而是记忆化搜索,不过是顺便练了就凑进来了
中文题目,所以就不解释题意了,直接上代码吧
#include <bits/stdc++.h>#define N 25#define INF 0x3f3f3f3f#define LL long long#define eps 1e-9#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double dp[N][N][8][8][8];void init();double dfs(int x,int y,int a,int b,int c);int main(){ ios::sync_with_stdio(false); int t,x,y,a,b,c; cin>>t; while(t--){ cin>>x>>y>>a; init(); cout<<fixed<<setprecision(6)<<dfs(x,y,a,0,0)<<endl; } return 0;}void init(){ for(int i=0;i<N;++i){ for(int j=0;j<N;++j){ for(int k=0;k<8;++k){ for(int l=0;l<8;++l){ for(int o=0;o<8;++o){ dp[i][j][k][l][o]=-1; } } } } }}double dfs(int x,int y,int a,int b,int c){ if(dp[x][y][a][b][c]!=-1){ return dp[x][y][a][b][c]; } if(y==0){ return dp[x][y][a][b][c]=1.0; } if(x<y||!x){ dp[x][y][a][b][c]=0; }else{ dp[x][y][a][b][c]=1.0/(a+b+c+1)*dfs(x-1,y-1,a,b,c); if(a+b+c<7){ if(a>0){ dp[x][y][a][b][c]+=1.0*a/(a+b+c+1)*dfs(x-1,y,a,b+1,c); } if(b>0){ dp[x][y][a][b][c]+=1.0*b/(a+b+c+1)*dfs(x-1,y,a+1,b-1,c+1); } }else{ if(a>0){ dp[x][y][a][b][c]+=1.0*a/(a+b+c+1)*dfs(x-1,y,a-1,b+1,c); } if(b>0){ dp[x][y][a][b][c]+=1.0*b/(a+b+c+1)*dfs(x-1,y,a,b-1,c+1); } } if(c>0){ dp[x][y][a][b][c]+=1.0*c/(a+b+c+1)*(x-1,y,a,b,c-1); } } return dp[x][y][a][b][c];}
关于为什么一般期望倒推,概率顺推的思考(一)
我一开始猜想是不是因为类似0-1背包要从二维优化到一维的时候一样,要保证考虑的子问题的解是不会重复迭代的,拿0-1背包举例
//错误的代码for(int i=cost;i<volume;++i){ dp[i]=max(dp[i-cost]+value,dp[i]);}//正确的代码for(int i=volume;i>=cost;--i){ dp[i]=max(dp[i-cost]+value,dp[i]);}
看代码可以直接想到,如果用前一个代码,那么在这一轮循环中可能出现一个物品拿了多次的情况,这就是被重复迭代了的结果,那么我们类似地通过代码考虑一下期望顺推的情况,这里以第一题为例。
但是我写了一下代码……
并不是这样的……
然后问了一下学长,如果是期望的情况,推导的就是由终了状态到起始状态的逆推,而终了状态到终了状态的期望是0,建立在这样的基础上逆推就是正确的答案。
而概率的话,终了状态的信息位置,无法逆推。
之后再补完,感觉还是没有完全弄懂。
- 概率DP学习笔记(一)
- 《概率机器人》学习笔记一
- 概率DP问题整理(一)
- 状压DP学习笔记(一)
- 期望(二)—— 概率与期望 DP 学习笔记
- [学习笔记]概率与期望dp做题总结
- 概率图模型学习(一):概率图矩阵分解
- [笔记] 概率DP ZOJ-3822
- 深度学习--概率图模型(一)
- 12.1-12.2概率dp学习~
- 概率图模型(PGM)学习笔记(一)动机与概述
- 概率图模型(PGM)学习笔记(一)动机与概述
- 概率图模型(PGM)学习笔记(一)动机与概述
- Khan公开课 - 概率学习笔记(一)独立事件、相依事件和排列组合
- 概率论与数理统计学习笔记一:事件的概率
- poj3071(概率DP)
- codeforces148D(概率DP)
- poj2151(概率DP)
- Java IO流
- Mybatis---原生态JDBC编程的问题
- Java+Selenium3方法篇25-isDisplayed方法
- jvm类加载
- Docker exec 命令
- 概率DP学习笔记(一)
- 牛顿迭代法
- 我的学习之路_第十八章_SQL语句
- [LeetCode] 606. Construct String from Binary Tree
- C#无标题栏窗体鼠标拖动
- 徒手撸个http服务器(一)
- Nfc--Handover相关java层源码分析
- 选择排序算法
- [转] 卷积