期望DP的一些题
hdu3853
题意:
- 题意:有一个迷宫r行m列,开始点在[1,1]现在要走到[r,c]
- 对于在点[x,y]可以打开一扇门走到[x+1,y]或者[x,y+1]
- 消耗2点魔力
- 问平均消耗多少魔力能走到[r,c]
英语不好,只能看别人的翻译
网上题解有些没说清楚,不管是走还是停,都会花费两点魔力值。
很裸的期望DP,我记得有句很经典的话:终点是一切概率之末,确是一切期望之始。
很好懂的Dp方程式:
Dp[i][j]=(Dp[i+1][j]+2)*P1+(Dp[i][j+1]+2)*P2+(Dp[i][j]+2)*P0
注意到P1+P2+P0=1;移相即可化简
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,m; double dp[1005][1005],p[1005][1005][3]; int main() { while(~scanf("%d %d",&n,&m)) { memset(dp,0,sizeof(dp)); memset(p,0,sizeof(p)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%lf %lf %lf",&p[i][j][0],&p[i][j][1],&p[i][j][2]); } dp[n][m]=0; for(int i=n;i>=1;i--) for(int j=m;j>=1;j--) { if(i==n&&j==m) continue; if(p[i][j][0]==1) continue; dp[i][j]=(p[i][j][1]*dp[i][j+1]+p[i][j][2]*dp[i+1][j]+2)/(1-p[i][j][0]); } printf("%.3f\n",dp[1][1]); } }
hdu4405:这道题更加简单,不用考虑留下来的概率,该怎么做就怎么做
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; int n,m,vis[100010]; double dp[100010]; int main() { while(~scanf("%d %d",&n,&m)&&n) { memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); for(int i=1;i<=m;i++) { int a,b; scanf("%d %d",&a,&b); vis[a]=b; } for(int i=n-1;i>=0;i--) if(vis[i]) dp[i]=dp[vis[i]];else for(int j=1;j<=6;j++) dp[i]+=(dp[i+j]+1)/6.0; printf("%.4f\n",dp[0]); } }
hdu4336
其实懂了思路后很好写,反正就是常规套路。
ans=sigma(last_ans+cost)*probablity+ans*probablity of remain
#include<iostream> #include<cstdio> #include<algorithm> double dp[1100005],p[1005]; int n; using namespace std; int main() { while(~scanf("%d",&n)) { for(int i=1;i<=n;i++) { scanf("%lf",&p[i]); } int l=(1<<n)-1; dp[l]=0; for(int sta=l-1;sta>=0;sta--) { double sum=0,tot=0; for(int j=1;j<=n;j++) if(!(sta&(1<<j-1))) sum+=p[j]*(dp[sta|(1<<j-1)]),tot+=p[j]; sum+=1; dp[sta]=sum/(tot); } printf("%.5f\n",dp[0]); } }
bzoj4832
Input
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; double vis[55][8][8][8],f[55][8][8][8]; int T,k,a,b,c; double pro(int x,int a,int b,int c) { return double(x)/(a+b+c+1); } double dp(int k,int a,int b,int c) { if(!k) return 0; c=min(c,7-a-b); if(vis[k][a][b][c]) return f[k][a][b][c]; vis[k][a][b][c]=1; f[k][a][b][c]+=pro(1,a,b,c)*(dp(k-1,a,b,c)+1); if(a)f[k][a][b][c]+=pro(a,a,b,c)*dp(k-1,a-1,b,c); if(b)f[k][a][b][c]+=pro(b,a,b,c)*dp(k-1,a+1,b-1,c+1); if(c)f[k][a][b][c]+=pro(c,a,b,c)*dp(k-1,a,b+1,c); return f[k][a][b][c]; } int main() { scanf("%d",&T); while(T--) { memset(vis,0,sizeof(vis)); memset(f,0,sizeof(f)); scanf("%d %d %d %d",&k,&a,&b,&c); printf("%.2f",dp(k,a,b,c)); } }
bzoj1076
【bzoj1076】[SCOI2008]奖励关
2014年2月7日4,0871
Description
你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃)。 宝物一共有n种,系统每次抛出这n种宝物的概率都相同且相互独立。也就是说,即使前k-1次系统都抛出宝物1(这种情况是有可能出现的,尽管概率非常小),第k次抛出各个宝物的概率依然均为1/n。 获取第i种宝物将得到Pi分,但并不是每种宝物都是可以随意获取的。第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过一次,才能吃第i种宝物(如果系统抛出了一个目前不能吃的宝物,相当于白白的损失了一次机会)。注意,Pi可以是负数,但如果它是很多高分宝物的前提,损失短期利益而吃掉这个负分宝物将获得更大的长期利益。 假设你采取最优策略,平均情况你一共能在奖励关得到多少分值?
Input
第一行为两个正整数k和n,即宝物的数量和种类。以下n行分别描述一种宝物,其中第一个整数代表分值,随后的整数依次代表该宝物的各个前提宝物(各宝物编号为1到n),以0结尾。
Output
输出一个实数,保留六位小数,即在最优策略下平均情况的得分。
比较明显的壮压+DP的期望题
#include<cstdio> #include<iostream> using namespace std; double F[101][65536]; int N,K,t; int v[20],d[20],p[20]; int main() { for(int i=1;i<=16;i++)p[i]=1<<(i-1); scanf("%d%d",&N,&K); for(int i=1;i<=K;i++) { scanf("%d%d",&v[i],&t); while(t) { d[i]+=p[t]; scanf("%d",&t); } } for(int i=N;i;i--) for(int j=0;j<=p[K+1]-1;j++) { for(int k=1;k<=K;k++) if((d[k]&j)==d[k]) F[i][j]+=max(F[i+1][j],F[i+1][j|p[k]]+v[k]); else F[i][j]+=F[i+1][j]; F[i][j]/=K; } printf("%.6lf",F[1][0]); return 0; }