2011年北京ACM-ICPC区域赛改编题-----食堂----题解(一个非常好的概率dp)

目录

食堂

题目描述

输入描述:

输出描述:

输入

输出

备注:

思路解析:

代码实现:


 E-食堂

食堂

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

吉吉国王偶尔会回想起自己的高中时代。在吉吉国王的高中时代,下课后冲向食堂是每个学生的基本操作,但是总得有人失败,为什么不能是我,实际上吉吉国王在打饭这件事上也是失败过很多次,比如没带饭卡,走错窗口,甚至食堂关门。
吉吉国王的高中食堂排队可以看成一个长度为nnn的队列,一开始吉吉国王站在mmm这个位置上,一般来说,窗口前的第一个人在打饭的时候会发生四种情况。
第一种情况是打饭的时候窗口没人,这个时候要等待一会儿,发生的概率是p1p_1p1​。
第二种情况是发现自己没带饭卡,这个时候就要回去拿饭卡并且排到了队列的末尾,发生的概率是p2p_2p2​。(这里认为每个人只有在即将打饭的时候才会去摸饭卡,只有这时才有发现自己没带饭卡的机会。)
第三种情况是打饭成功,这个时候队列的长度减一,发生的概率是p3p_3p3​。
第四种情况是食堂关门,这个时候大家都不能打饭了,发生的概率是p4p_4p4​。
吉吉国王老倒霉蛋了,经常在食堂关门的时候排在队伍的前面,因此他想知道这样的事件发生的概率。现在你需要告诉吉吉国王在食堂关门时他排在队伍的前kkk位的概率。

输入描述:

一行七个数表示n,m,k,p1,p2,p3,p4.

输出描述:

输出一个小数表示答案,小数点后保留五位。

示例1

输入

4 4 1 0.372818 0.318286 0.220035 0.0888615

输出

0.15428

备注:

1 <= k<=m<=n<=2000

0<=p1,p2,p3,p4<=1,p1+p2+p3+p4=1.

思路解析:

if j==1 
		f[i][j] = p1*f[i][j] + p2*f[i][i] + p4
p1 * dp[i][j]表示当前食堂每人停留在此状态,最后成功的概率,p2*f[i][i]表示自己没带饭卡回到队尾最后成功的概率,p4表示当前食堂关门,成功。
if j>=2&&j<=k 
		f[i][j]=p1*f[i][j]+p2*f[i][j-1]+p3*f[i-1][j-1]+p4
p1 * dp[i][j]表示当前食堂每人停留在此状态,最后成功的概率,p2*f[i][j-1]表示当前队伍第一个人没带饭卡回到队尾最后成功的概率,p3*f[i-1][j-1]表示当前队伍第一个人打饭成功,最后成功的概率,p4表示当前食堂关门,成功。
if j>k
		f[i][j]=p1*f[i][j]+p2*f[i][j-1]+p3*f[i-1][j-1]
p1 * dp[i][j]表示当前食堂每人停留在此状态,最后成功的概率,p2*f[i][j-1]表示当前队伍第一个人没带饭卡回到队尾最后成功的概率,p3*f[i-1][j-1]表示当前队伍第一个人打饭成功,最后成功的概率。

由于两边都有f[i][j],那么经过移项合并同类项可以得到:
j==1
		f[i][1]=p2/(1-p1)*f[i][i]+p4/(1-p1)
j<=k
		f[i][j]=p2/(1-p1)*f[i][j-1]+p3/(1-p1)*f[i-1][j-1]+p4/(1-p1)
j<=n
		f[i][j]=p2/(1-p1)*f[i][j-1]+p3/(1-p1)*f[i-1][j-1]

因此我们令:
a=p2/(1-p1)
b=p3/(1-p1)
c=p4/(1-p1)

观察式子发现当计算到f[i][j]的时候,f[i-1][j-1]已经被算出来了,因此我们可以将其看作常数项,继续定义化简:
j==1 
		d[j]=c
j<=k
		d[j]=b*f[i-1][j-1]+c
j<=n
		d[j]=b*f[i-1][j-1]


最后式子就被化简为了:
j==1
		f[i][1] = a*f[i][i]+d[1]
j>=2
		f[i][j]=a*f[i][i-1]+d[j]

多列几项f[i][j]可以发现从第一个式子开始全部带进最后一个式子可以算出f[i][i],
比如:i == 3时,
dp[i][1] = a * dp[i][i] + d[1]
dp[i][2] = a * dp[i][1] + d[2]
dp[i][3] = a * dp[i][2] + d[3] = dp[i][i]
最后将前两项带入最后一项可以进行化简,最后发现规律,这样就可以快速求出dp[i][i]
因此先算出f[i][i]:
f[i][i]=(∑p[i-k]d[k])/(1-p[i])

然后再递推求出其他f[i][j]即可~

代码实现:

import java.util.Scanner;

/**
 * @ProjectName: study3
 * @FileName: Ex4
 * @author:HWJ
 * @Data: 2023/11/27 21:15
 */
public class Ex4 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int m = input.nextInt();
        int k = input.nextInt();
        double p1 = input.nextDouble();
        double p2 = input.nextDouble();
        double p3 = input.nextDouble();
        double p4 = input.nextDouble();
        double[][] dp = new double[n + 1][n + 1];
        double[] p = new double[n + 1];
        double a = p2 / (1 - p1);
        double b = p3 / (1 - p1);
        double c = p4 / (1 - p1);
        p[0] = 1;
        for (int i = 1; i <= n; i++) {
            p[i] = p[i - 1] * a;
        }
        dp[1][1] = p4 / (1 - p1 - p2);
        double[] d = new double[n + 1];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                if (j == 1) {
                    d[j] = c;
                } else if (j >= 2 && j <= k) {
                    d[j] = b * dp[i - 1][j - 1] + c;
                } else if (j > k) {
                    d[j] = b * dp[i - 1][j - 1];
                }
            }
            for (int j = 1; j <= i; j++) {
                dp[i][i] += d[j] * p[i - j];
            }
            dp[i][i] /= (1.0 - p[i]);
            for (int j = 1; j <= i; j++) {
                if (j == 1){
                    dp[i][j] = a * dp[i][i] + d[j];
                }else {
                    dp[i][j] = a * dp[i][j - 1] + d[j];
                }
            }

        }
        System.out.printf("%.10f", dp[n][m]);
    }
}

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值