期望DP入门

雨点打透心脏的1/2处 2024-03-17 17:05 141阅读 0赞

期望DP一般步骤:

1.模拟过程,找出线性性质,作为阶段(这本质上也是线性DP)

2.涉及DP状态

原则:

体现线性性质

体现边权

根据对期望有无贡献来设计状态

一般在状态设计时需要倒着设计

3.转移

根据边权转移

分为对期望贡献的区别来分开计算贡献

不同情况的概率贡献不一样,因此期望贡献也不一样,因此需要分开考虑

考虑权值时可以递归地考虑

8aedd868b51e4c22ad1ef8f03de6e460.png

1.

题意:

849b1d6c81334122bc8adf49cf25ff7b.png

9067cea865a8430c8502d436a092c7bc.png

思路:

设一直选直到1号球被选中的期望选择次数是E

可以列方程:

E=1/n+(1-1/n)*(E+1)

求出E=n

2.

85c1b6ee7e1040678c7781820be937aa.png

4732c10d1b0f4547ae470dd4f4e4919d.png

思路:

首先概率是一定的

在选的过程中,如果选到了1-K号的某个没被选过的球,剩下就选K-1个,因此线性性质就体现在 剩下还要选多少球 中

所以这个可以记成一个状态

所以最终设出来的状态只有一维:

设dp[i]为还剩下i个球选的选择次数的期望

然后考虑转移

选择的决策可以分为,选对选择次数有贡献的,和选对选择次数没有贡献的

选没被选过且编号在1-K的,就是对次数有贡献的,否则就是没有贡献的

前者的期望贡献:dp[i]+=k/n*(dp[i-1]+1),边权为1

后者的期望贡献:dp[i]+=(1-k/n)*(dp[i-1]+1),边权为1

Code:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int mxn=1e6+10;
  5. const int mxe=1e3+10;
  6. const int mod=1e9+7;
  7. int N,Q,K;
  8. int E[mxn];
  9. int ksm(int a,int b,int mod){
  10. int res=1;
  11. while(b){
  12. if(b&1) res=(res*a)%mod;
  13. a=(a*a)%mod;
  14. b>>=1;
  15. }
  16. return res;
  17. }
  18. void solve(){
  19. cin>>N>>Q;
  20. E[1]=N;
  21. for(int i=2;i<=N;i++) E[i]=(E[i-1]+N*ksm(i,mod-2,mod)%mod)%mod;
  22. while(Q--){
  23. cin>>K;
  24. cout<<E[K]%mod<<'\n';
  25. }
  26. }
  27. signed main(){
  28. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  29. int __=1;//cin>>__;
  30. while(__--)solve();return 0;
  31. }

3.

题意:

03c073464c584c3897f37cdbdf123a63.png

29a6cfb5c4914f0daa420325109cefce.png

思路:

首先看它的决策是什么时,对期望产生贡献

当它选红色且没有被选过的小球时,对期望有贡献,否则就是没有贡献

设计的状态需要体现期望贡献

红色:m

没有被选过:用k表示还需要选多少球,这也体现了线性性质

因为已经选过的红色小球对期望没有贡献,因此可以把选过的红色小球去掉

因此设dp[k][m]为还剩下m个红色小球,且还需要选k个不同编号的红色小球的选择次数的期望

转移:

对期望有贡献的部分:m/n*(dp[k-1][m-1]+1)

对期望无贡献的部分:(1-m/n)*(dp[k][m]+1)

然后解个方程就出来了

dp[k][m]=dp[k-1][m-1]+n/m

Code:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int mxn=1e2+10;
  5. const int mxe=1e3+10;
  6. const int mod=1e9+7;
  7. int N,M,Q,K;
  8. int E[mxn][mxn];
  9. int ksm(int a,int b,int mod){
  10. int res=1;
  11. while(b){
  12. if(b&1) res=(res*a)%mod;
  13. a=(a*a)%mod;
  14. b>>=1;
  15. }
  16. return res;
  17. }
  18. void solve(){
  19. while(cin>>N>>M>>Q){
  20. E[1][1]=N;
  21. for(int i=1;i<=M;i++){
  22. for(int j=1;j<=i;j++){
  23. E[i][j]=(E[i-1][j-1]+N*ksm(i,mod-2,mod)%mod)%mod;
  24. }
  25. }
  26. while(Q--){
  27. cin>>K;
  28. cout<<E[M][K]%mod<<'\n';
  29. }
  30. }
  31. }
  32. signed main(){
  33. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  34. int __=1;//cin>>__;
  35. while(__--)solve();return 0;
  36. }

4.

题意:

cfeafc4c13d3444280b26235e4867128.png

思路:

分为尝试成功和尝试失败两部分进行转移

Code:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int mxn=1e7+10;
  5. const int mxe=1e3+10;
  6. const int mod=1e9+7;
  7. int N;
  8. int a[mxn],Fac[mxn];
  9. int ksm(int a,int b,int mod){
  10. int res=1ll;
  11. while(b){
  12. if(b&1) res=(res*a)%mod;
  13. a=(a*a)%mod;
  14. b>>=1;
  15. }
  16. return res%mod;
  17. }
  18. void F_init(){
  19. Fac[0]=1;
  20. for(int i=1;i<mxn;i++) Fac[i]=(Fac[i-1]*i)%mod;
  21. }
  22. void solve(){
  23. int sum=0;
  24. for(int i=1;i<=10;i++){
  25. cin>>a[i];
  26. sum+=a[i];
  27. }
  28. int ans=Fac[sum];
  29. for(int i=1;i<=10;i++) ans=ans*ksm(Fac[a[i]],mod-2,mod)%mod;
  30. cout<<ans<<'\n';
  31. }
  32. signed main(){
  33. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  34. int __=1;//cin>>__;
  35. F_init();
  36. while(__--)solve();return 0;
  37. }

5.

题意:

b5471e2401484854934335f6767ae799.png

思路:

因为计算期望时需要计算概率,计算概率需要n和s这两个参数

因此在设计状态时需要用这两个参数

设dp[n][s]为,已经发现了n种bug,s个子系统的天数期望

然后可以分为4种情况来转移,每种情况的概率贡献不一样

13b669af090c42e88488cd6ae4c2762b.png

Code:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int mxn=1e3+10;
  5. const int mxe=1e6+10;
  6. const int mod=1e9+7;
  7. const int Inf=1e18;
  8. int N,S;
  9. double dp[mxn][mxn];
  10. void solve(){
  11. cin>>N>>S;
  12. dp[N][S]=0;
  13. for(int i=N;i>=0;i--){
  14. for(int j=S;j>=0;j--){
  15. if(i==N&&j==S) continue;
  16. dp[i][j]=(N*S+(N-i)*j*dp[i+1][j]+i*(S-j)*dp[i][j+1]+(N-i)*(S-j)*dp[i+1][j+1])/(N*S-i*j);
  17. }
  18. }
  19. cout<<fixed<<setprecision(4)<<dp[0][0]<<'\n';
  20. }
  21. signed main(){
  22. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  23. int __=1;//cin>>__;
  24. while(__--)solve();return 0;
  25. }

6.

59862d90ab31480f8726764bea453378.png

思路:

同样的,也是设期望时间,然后列方程(转移)

设逃离迷宫的期望时间为E

01fc994bf8c1423bae064afc14d72f40.png

Code:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int mxn=1e2+10;
  4. const int mxe=1e3+10;
  5. int N,idx=0;
  6. int x[mxn];
  7. void solve(){
  8. cin>>N;
  9. for(int i=1;i<=N;i++) cin>>x[i];
  10. int s1=0,s2=0,cnt=0;
  11. for(int i=1;i<=N;i++){
  12. if(x[i]<0){
  13. cnt++;
  14. s2+=(-x[i]);
  15. }else{
  16. s1+=x[i];
  17. }
  18. }
  19. int d1=(s1+s2);
  20. int d2=(N-cnt);
  21. int d=__gcd(d1,d2);
  22. d1/=d,d2/=d;
  23. if(cnt==N) cout<<"Case "<<++idx<<": inf"<<'\n';
  24. else cout<<"Case "<<++idx<<": "<<d1<<"/"<<d2<<'\n';
  25. }
  26. int main(){
  27. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  28. int __=1;cin>>__;
  29. while(__--)solve();return 0;
  30. }

7.

题意:

036cf3dbc8ce4e32a2cd39bf157b6265.png

bdf6dbe028b043618197eae4ba3eed41.png

思路:

这个的线性性质很明显

设dp[i]为从位置 i 到位置 N 得到金子的价值的期望

它有min(6,N-i)种决策,只要不超过N就行

Code:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int mxn=1e2+10;
  5. const int mxe=1e6+10;
  6. const int mod=1e9+7;
  7. const int Inf=1e18;
  8. int tot=0;
  9. int N;
  10. int a[mxn];
  11. double dp[mxn];
  12. //从位置i到位置N的期望价值
  13. void solve(){
  14. cin>>N;
  15. for(int i=0;i<=N;i++) dp[i]=0.0;
  16. for(int i=1;i<=N;i++) cin>>a[i];
  17. dp[N]=0.0;
  18. for(int i=N-1;i>=1;i--){
  19. for(int k=1;k<=6;k++){
  20. double p=1.0/min((N-i)*1.0,6.0);
  21. dp[i]+=(dp[i+k]+1.0*a[i+k])*p;
  22. }
  23. }
  24. cout<<fixed<<setprecision(8)<<"Case "<<++tot<<":"<<" "<<dp[1]+a[1]<<'\n';
  25. }
  26. signed main(){
  27. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  28. int __=1;cin>>__;
  29. while(__--)solve();return 0;
  30. }

8.

58960f6d8714467db629938de379f33d.png

5a129810440345aea92b3e433800a40e.png

思路:

174f463f37714f4a86b4f9c6d53b8ceb.png

Code:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int mxn=1e5+10;
  5. const int mxe=1e5+10;
  6. const int mod=1e9+7;
  7. const int Inf=1e18;
  8. struct ty{
  9. int to,next;
  10. }edge[mxe<<2];
  11. int N,tot=0;
  12. int u,v;
  13. int head[mxn];
  14. double ans=0;
  15. void add(int u,int v){
  16. edge[tot].to=v;
  17. edge[tot].next=head[u];
  18. head[u]=tot++;
  19. }
  20. void G_init(){
  21. tot=0;
  22. for(int i=0;i<=N;i++){
  23. head[i]=-1;
  24. }
  25. }
  26. void dfs(int u,int fa,int dep){
  27. ans+=1.0/(1.0*dep);
  28. for(int i=head[u];~i;i=edge[i].next){
  29. if(edge[i].to==fa) continue;
  30. dfs(edge[i].to,u,dep+1);
  31. }
  32. }
  33. void solve(){
  34. cin>>N;
  35. G_init();
  36. for(int i=1;i<=N-1;i++){
  37. cin>>u>>v;
  38. add(u,v);
  39. add(v,u);
  40. }
  41. dfs(1,1,1);
  42. cout<<fixed<<setprecision(8)<<ans<<'\n';
  43. }
  44. signed main(){
  45. ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  46. int __=1;//cin>>__;
  47. while(__--)solve();return 0;
  48. }

发表评论

表情:
评论列表 (有 0 条评论,141人围观)

还没有评论,来说两句吧...

相关阅读

    相关 期望DP入门

    期望DP一般步骤: 1.模拟过程,找出线性性质,作为阶段(这本质上也是线性DP) 2.涉及DP状态 原则: 体现线性性质 体现边权 根据对期望有无贡献来设计状态

    相关 HDU 3853-LOOPS【期望DP

    题意:有一个R\C的迷宫,从(1,1)走到(R,C),每个格子给出停留在原地,向右走一格和向下走一格的概率,且每走一步需要2点能量,求最后所需要的能量期望。 题目链接:[ht