期望dp练习

例题:

Problem - I - Codeforces

题意:
n个俱乐部,m个奖牌,每个俱乐部可以拿这m个奖牌之一作为该俱乐部游戏的奖励。现在有一些人来参加游戏,游戏终止条件是所有奖牌都被收集,问你最小游戏次数的期望值是多少

思路:

1、我们可以发现在n个俱乐部均分m块奖牌的时候,会让游戏次数最小(为什么发现我也不知道)

2、但肯定有不能均分的,这时候我们就把所有的奖牌分为两个大类

a:不能被n个俱乐部均分的

b:能被n个俱乐部均分的

int a = n % m, b = m - a;

则个数分别为

int ca = (n + m - 1) / m, cb = n / m; // cnt

 设dp[i][j] 表示差 i 个第一类奖牌和 j 个第二类奖牌所需要的期望次数,dp[0][0] = 0

选取到新的奖牌的总概率 s 为 i * ca + j * cb;

再从总概率里分出选出a的概率和选出b的概率

long double s = i * ca + j * cb;
long double p1 = 1.0 * i * ca / s;
long double p2 = 1.0 * j * cb / s;

 有p1的概率从dp[i - 1][j] 传递, dp[i][j] += dp[i - 1][j]。需要主义的是 i 要大于零

if (i) dp[i][j] += (p1) * dp[i - 1][j];

p2同理

贴个总代码: 

#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int N = 2e6 + 10, mod = 998244353, INF = 0x3f3f3f3f;

long double dp[5010][5010];
void solve() {
    int n, m;
    cin >> n >> m;
    int a = n % m, b = m - a;
    int ca = (n + m - 1) / m, cb = n / m; // cnt

    dp[0][0] = 0;

    for (int i = 0; i <= a; i ++) {
        for (int j = 0; j <= b; j ++) {
            long double s = i * ca + j * cb;
            long double p1 = 1.0 * i * ca / s;
            long double p2 = 1.0 * j * cb / s;
            if (!i && !j) continue;
            dp[i][j] += n / s;
            if (i) dp[i][j] += (p1) * dp[i - 1][j];
            if (j) dp[i][j] += (p2) * dp[i][j - 1];
        }
    }
    cout << fixed << setprecision(10) << dp[a][b];
}
int main() {
    ios::sync_with_stdio(0), cin.tie(0);

    solve();

    return 0;
}

贴个记忆化搜索的

#include<bits/stdc++.h>
using namespace std;

int n,m;
long double dp[5002][5002];
int x;
int y;
int B;
int A;

long double dfs(int a,int b) {
    /*
        A:B 已经有了a:b个
    */
    if(dp[a][b]!=-1) {
        return dp[a][b];
    }
    if(a>A || b>B) {
        return 0;
    }

    //有用的个数=剩下没拿的部分
    long double s= (A-a)*x + (B-b)*(x+1);

    long double ans=0;

    //两种转移的概率
    long double p1= (long double)(A-a)*x/s;
    long double p2= (long double)(B-b)*(x+1)/s;

    //两种本身的期望 + 转移的期望步数1/p  (p=n/s)
    //这里其实应该更具体,是a-1本身的期望 + a-1转移的期望步数*p1。只是p1+p2==1所以最后直接+n/s
    ans+=( dfs(a+1,b) ) * p1;
    ans+=( dfs(a,b+1) ) * p2;
    ans+=(long double) n/s;

    return dp[a][b]=ans;
}
void solve() {
    for(int i=0;i<=5000;i++) {
        for(int j=0;j<=5000;j++) {
            dp[i][j]=-1;
        }
    }
    cin>>n>>m;
    /*
        两种,数量为a , b <--> x , x+1
    */

    x=n/m;
    y=n/m+1;
    B=n%m;
    A=m-n%m;

    dp[A][B]=0;
    long double ans=dfs(0,0);

    cout<<fixed<<setprecision(12)<<ans;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);


    int t=1;
    // cin>>t;
    while(t--)
    solve();
}

其他例题:

F-小红的扔骰子_牛客周赛 Round 42 (nowcoder.com)

J - Sushi (atcoder.jp)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值