期望DP入门

期望DP一般步骤:

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

2.涉及DP状态

原则:

体现线性性质

体现边权

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

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

3.转移

根据边权转移

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

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

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

1.

题意:

 

思路:

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

可以列方程:

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

求出E=n

2.

 

 思路:

首先概率是一定的

在选的过程中,如果选到了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:

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int mxn=1e6+10;
const int mxe=1e3+10;
const int mod=1e9+7;

int N,Q,K;
int E[mxn];

int ksm(int a,int b,int mod){
	int res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}
void solve(){
	cin>>N>>Q;
	E[1]=N;
	for(int i=2;i<=N;i++) E[i]=(E[i-1]+N*ksm(i,mod-2,mod)%mod)%mod;
	while(Q--){
		cin>>K;
		cout<<E[K]%mod<<'\n';
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int __=1;//cin>>__;
    while(__--)solve();return 0;
}

3.

题意:

 

 思路:

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

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

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

红色: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:

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int mxn=1e2+10;
const int mxe=1e3+10;
const int mod=1e9+7;

int N,M,Q,K;
int E[mxn][mxn];

int ksm(int a,int b,int mod){
	int res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}
void solve(){
	while(cin>>N>>M>>Q){
		E[1][1]=N;
		for(int i=1;i<=M;i++){
			for(int j=1;j<=i;j++){
				E[i][j]=(E[i-1][j-1]+N*ksm(i,mod-2,mod)%mod)%mod;
			}
		}
		while(Q--){
			cin>>K;
			cout<<E[M][K]%mod<<'\n';
		}
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int __=1;//cin>>__;
    while(__--)solve();return 0;
}

4.

题意:

思路:

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

Code:

#include <bits/stdc++.h>
 
#define int long long
 
using namespace std;
 
const int mxn=1e7+10;
const int mxe=1e3+10;
const int mod=1e9+7;
 
int N;
int a[mxn],Fac[mxn];
 
int ksm(int a,int b,int mod){
    int res=1ll;
    while(b){
        if(b&1) res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res%mod;
}
void F_init(){
    Fac[0]=1;
    for(int i=1;i<mxn;i++) Fac[i]=(Fac[i-1]*i)%mod;
}
void solve(){
    int sum=0;
    for(int i=1;i<=10;i++){
        cin>>a[i];
        sum+=a[i];
    }
    int ans=Fac[sum];
    for(int i=1;i<=10;i++) ans=ans*ksm(Fac[a[i]],mod-2,mod)%mod;
    cout<<ans<<'\n';
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int __=1;//cin>>__;
    F_init();
    while(__--)solve();return 0;
}

5.

题意:

思路:

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

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

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

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

Code:

#include <bits/stdc++.h>
 
#define int long long
 
using namespace std;
 
const int mxn=1e3+10;
const int mxe=1e6+10;
const int mod=1e9+7;
const int Inf=1e18;
 
int N,S;
double dp[mxn][mxn];
 
void solve(){
    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]=(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);
        }
    }
    cout<<fixed<<setprecision(4)<<dp[0][0]<<'\n';
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int __=1;//cin>>__;
    while(__--)solve();return 0;
}

6.

思路:

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

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

 Code:

#include <bits/stdc++.h>
 
using namespace std;
 
const int mxn=1e2+10;
const int mxe=1e3+10;
 
int N,idx=0;
int x[mxn];
 
void solve(){
    cin>>N;
    for(int i=1;i<=N;i++) cin>>x[i];
    int s1=0,s2=0,cnt=0;
    for(int i=1;i<=N;i++){
        if(x[i]<0){
            cnt++;
            s2+=(-x[i]);
        }else{
            s1+=x[i];
        }
    }
    int d1=(s1+s2);
    int d2=(N-cnt);
    int d=__gcd(d1,d2);
    d1/=d,d2/=d;
    if(cnt==N) cout<<"Case "<<++idx<<": inf"<<'\n';
    else cout<<"Case "<<++idx<<": "<<d1<<"/"<<d2<<'\n';
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int __=1;cin>>__;
    while(__--)solve();return 0;
}

7.

题意:

 

思路:

这个的线性性质很明显

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

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

Code:

#include <bits/stdc++.h>
 
#define int long long
 
using namespace std;
 
const int mxn=1e2+10;
const int mxe=1e6+10;
const int mod=1e9+7;
const int Inf=1e18;
 
int tot=0;
int N;
int a[mxn];
double dp[mxn];
//从位置i到位置N的期望价值
 
void solve(){
    cin>>N;
    for(int i=0;i<=N;i++) dp[i]=0.0;
    for(int i=1;i<=N;i++) cin>>a[i];
    dp[N]=0.0;
    for(int i=N-1;i>=1;i--){
        for(int k=1;k<=6;k++){
            double p=1.0/min((N-i)*1.0,6.0);
            dp[i]+=(dp[i+k]+1.0*a[i+k])*p;
        }
    }
    cout<<fixed<<setprecision(8)<<"Case "<<++tot<<":"<<" "<<dp[1]+a[1]<<'\n';
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int __=1;cin>>__;
    while(__--)solve();return 0;
}

 

8.

 

思路:

Code:

#include <bits/stdc++.h>
 
#define int long long
 
using namespace std;
 
const int mxn=1e5+10;
const int mxe=1e5+10;
const int mod=1e9+7;
const int Inf=1e18;
 
struct ty{
    int to,next;
}edge[mxe<<2];
 
int N,tot=0;
int u,v;
int head[mxn];
double ans=0;
 
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void G_init(){
    tot=0;
    for(int i=0;i<=N;i++){
        head[i]=-1;
    }
}
void dfs(int u,int fa,int dep){
    ans+=1.0/(1.0*dep);
    for(int i=head[u];~i;i=edge[i].next){
        if(edge[i].to==fa) continue;
        dfs(edge[i].to,u,dep+1);
    }
}
void solve(){
    cin>>N;
    G_init();
    for(int i=1;i<=N-1;i++){
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs(1,1,1);
    cout<<fixed<<setprecision(8)<<ans<<'\n';
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int __=1;//cin>>__;
    while(__--)solve();return 0;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值