Codeforces 28C Bath Queue 【计数类DP】*

博客详细分析了Codeforces 28C Bath Queue问题,这是一个关于概率和动态规划的题目。题意为n个人随机进入m个有不同水龙头的房间,目标是最小化最长队列的期望长度。通过考虑每个房间的进入概率和最大队列人数,使用动态规划策略来解决问题。博主指出可以枚举最大队列长度和人数,分别讨论最大值出现在当前房间和不在当前房间的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Codeforces 28C Bath Queue


LINK


简要题意:有 n 个人等概率随机进入 m 个房间,一个房间可以有多个人,第 i 个房间有 ai 个水龙头,在一个房间的人要去排队装水,他们会使得最长的队尽可能小,求所有房间中最长队列长度的期望

Mark一个很好的blog


然后快考虑这题咋做

你先看n的范围感觉是 n5 n 5 的DP

因为对于每个人,进入每个房间的概率是相同的,所以我们只用每种情况的权值*每种情况的出现概率就可以了,然后又因为每种情况的权值只是最多人数,所以我们只需要统计最多人数的不同情况的方案数就可以了

首先考虑一下每次所有人进到房间之后的状态,一定有一个或多个房间是存在最大值的,然后我们考虑枚举最大值,又因为人进入的顺序没有关系,直接考虑总人数和最后结果的影响就行了

然后我们记 dpi,j,k d p i , j , k 表示前i个房间进入了j个人,最多一个房间排队人数最长是k的方案数

然后我们直接按顺序枚举房间,这里有一个小细节,就是枚举房间的顺序无关紧要,因为我们每次只需要考虑的是当前的最大值出现在这个房间还是其他房间。

然后枚举人数,然后分两种情况
1:最大值出现在这个房间
2:最大值不出现在这个房间
进行讨论,在每个讨论里边枚举一下这个房间进来多少人就好了

感觉应该不难理解了


#include<bits/stdc++.h>
using namespace std;
#define N 110
#define fu(a,b,c) for(int a=b;a<=c;++a)
#define fd(a,b,c) for(int a=b;a>=c;--a)
double dp[N][N][N]={0};
double c[N][N]={0};
int a[N],n,m;
int main(){
    scanf("%d%d",&n,&m);
    fu(i,1,m)scanf("%d",&a[i]);
    fu(i,0,n+m)c[i][0]=1;
    fu(i,1,n+m)
        fu(j,1,i)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    fu(i,0,m)dp[i][0][0]=1;
    fu(i,1,m)
        fu(j,1,n)
            fu(k,0,j){
                //case1: 当前的房间到达上线
                int l=max(a[i]*(k-1)+1,0),r=a[i]*k;
                fu(w,0,k)
                    fu(p,l,min(r,j))
                        dp[i][j][k]+=dp[i-1][j-p][w]*c[n-j+p][p];
                //case2: 前面某个房间到达上限
                fu(p,0,min(l-1,j))
                    dp[i][j][k]+=dp[i-1][j-p][k]*c[n-j+p][p];
            }
    double ans=0;
    fu(i,1,n)ans+=dp[m][n][i]*i;
    fu(i,1,n)ans/=(double)m;
    printf("%.10lf",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值