ICPC-概率DP-ZOJ3329(概率DP+解未知数) POJ3744(概率DP+矩阵快速幂) HDU4089 HDU4035 HDU

本文深入探讨概率DP算法,通过解析多个经典题目,如ZOJ3329、POJ3744等,详细讲解了概率DP的原理与应用。文章提供了完整的代码示例,涵盖区间存活概率计算、排队系统故障概率预测等实际问题。

狂补概率DP
为此,献上一个优秀博主的链接
https://www.cnblogs.com/Paul-Guderian/p/7624039.html?tdsourcetag=s_pcqq_aiomsg

这题我先留个坑。晚点补题解
ZOJ3329 https://vjudge.net/problem/ZOJ-3329
这题注意:maxn必须设700+,因为,他说数值可能大于n(500),我觉得1000肯定够

#include<bits/stdc++.h>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll mod=998244353;
const int maxn=700;
double P[maxn+5];
double aa[maxn+5],bb[maxn+5];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,k1,k2,k3,a,b,c;
        scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
        mem(P,0);
        mem(aa,0);
        mem(bb,0);
        double temp=1.0/(k1*k2*k3);
        for(int i=1;i<=k1;i++){
            for(int j=1;j<=k2;j++){
                for(int k=1;k<=k3;k++){
                    if(i!=a||j!=b||k!=c)P[i+j+k]+=temp;
                }
            }
        }
        for(int i=n;i>=0;i--){
            aa[i]=temp;bb[i]=1.0;
            for(int j=3;j<=(k1+k2+k3);j++){
                aa[i]+=P[j]*aa[i+j];
                bb[i]+=P[j]*bb[i+j];
            }
        }
        double ans=bb[0]/(1.0-aa[0]);
        //cout<<ans<<endl;
        printf("%.15f\n",ans);
    }
    return 0;
}

POJ3744 https://vjudge.net/problem/POJ-3744
n<=1e8 数据量大,不适合用数组,同时雷少,仅不到10个,适合分段。段就是相邻的雷之间的区间。
答案是
累乘每个不同区间活着的概率
区间活着的概率,表示,i-1活着,但是,到i就死了。

#include<algorithm>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll mod=998244353;
const int maxn=700;
ll a[15];
const int g=2;
struct mx{
    double v[g][g];
    mx(){mem(v,0);}
    mx operator *(mx &t){
        mx res;
        mem(res.v,0);
        for(int i=0;i<g;i++){
            for(int j=0;j<g;j++){
                for(int k=0;k<g;k++){
                    res.v[i][j]+=v[i][k]*t.v[k][j];
                }
            }
        }
        return res;
    }
}mmmx;
mx power(mx a,int b){
    mx ans;
    mem(ans.v,0);
    for(int i=0;i<g;i++)ans.v[i][i]=1;
    while(b>0){
        if(b&1)ans=ans*a;
        b=b>>1;
        a=a*a;
    }
    return ans;
}
int main(){
    int n;
    double p;
    while(scanf("%d%lf",&n,&p)!=EOF){
        a[0]=0;
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        sort(a,a+n+1);
        mmmx.v[0][0]=p;
        mmmx.v[0][1]=1.0-p;
        mmmx.v[1][0]=1;
        mmmx.v[1][1]=0;
        mx temp;
        double ans=1.0;
        for(int i=1;i<=n;i++){
            if(a[i]!=a[i-1]){
                temp=power(mmmx,a[i]-a[i-1]-1);
                ans*=(1.0-temp.v[0][0]);
            }
        }
        printf("%.7f\n",ans);
    }
    return 0;
}

接下来的这些是其他概率dp,前面的链接里的博主给的。

HDU4089
https://vjudge.net/problem/HDU-4089
输入n,m表示一款注册账号时,小明现在在队伍中的第m个位置有n个用户在排队。每处理一个用户的信息时(指处在队首的用户),可能会出现下面四种情况:
1.处理失败,重新处理,处理信息仍然在队头,发生的概率为p1;
2.处理错误,处理信息到队尾重新排队,发生的概率为p2;
3.处理成功,队头信息处理成功,出队,发生的概率为p3;
4.服务器故障,队伍中所有信息丢失,发生的概率为p4;
问当他前面的信息条数不超过k-1同时服务器故障的概率。(1<=n,m<=2000)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const double eps=1e-9;
const int maxn=2000;
float dp[maxn+5][maxn+5];//用i行,j列,使用k步   离结束还需要的期望天数
float B[maxn+5];
int main(){
    int n,m,k;
    double p1,p2,p3,p4,k1,k2,k3;
	while(~scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)){
       if(p4<eps){printf("0.00000\n");continue;}
       mem(dp,0); //最后的n,m,k(任意多)时候,离结束的天数是0天(dp[n][m][maxn+5])
       k1=p2/(1-p1);
       k2=p3/(1-p1);
       k3=p4/(1-p1);
       int cnt=0;//进行了几次判断
       dp[cnt][1]=p4/(1-p1-p2);//(cnt局后)故障占(成功和故障)的概率
       for(int i=2;i<=n;i++){//n个用户,需要至少n-1次局
          double sum=0,p=1.0;
          //正常部分的判断
          for(int j=1;j<=k;j++)B[j]=k2*dp[cnt][j-1]+k3;
          for(int j=k+1;j<=i;j++)B[j]=k2*dp[cnt][j-1];
          //对特殊部分的判断
          for(int j=1;j<=i;j++){
            sum=sum*k1+B[j];
            p*=k1;//i局之后,队首到队尾的概率
          }
          //新一局的特殊的第一个点,1-
          cnt++;
          dp[cnt][1]=k1*sum/(1-p)+k3;//sum就是dp[i][i]
          //更新其他普通的dp节点
          for(int j=2;j<=i;j++)dp[cnt][j]=k1*dp[cnt][j-1]+B[j];
       }
       printf("%.5f\n",dp[cnt][m]);//到m位置,已经有cnt个人/局时需要的概率
	}
	return 0;
}

HDU4035
https://vjudge.net/problem/HDU-4035

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const double eps=1e-9;
const int maxn=1e4;
double bac[maxn+5],en[maxn+5],A[maxn+5],B[maxn+5],C[maxn+5];
int head[maxn+5];
struct E{
  int v;
  int nex;
}e[maxn<<1];
int n,m,k,t;
void add(int u,int v){
    e[k].v=v;
    e[k].nex=head[u];
    head[u]=k++;
}
double ab(double x){return x<0?-x:x;}
bool dfs(int u,int fa){
    if(e[head[u]].nex<0&&u!=1){//叶子节点
        A[u]=bac[u];
        B[u]=C[u]=1-bac[u]-en[u];
        return 1;
    }
    double sumA=0,sumB=0,sumC=0;
    int m=0;//非叶子节点
    for(int i=head[u],v=e[i].v;i!=-1;i=e[i].nex,v=e[i].v){
        if(++m&&v!=fa){
            if(!dfs(v,u))return 0;
            sumA+=A[v],sumB+=B[v],sumC+=(C[v]);
        }
    }
    //更新A,B,C
    double p=(1-bac[u]-en[u]);
    if(ab(1-p/m*sumB)<eps)return 0;
    A[u]=(bac[u]+p/m*sumA)/(1-p/m*sumB);
    B[u]=(p/m)/(1-p/m*sumB);
    C[u]=(p+p/m*sumC)/(1-p/m*sumB);
    return 1;
}
int main(){
    double p1,p2,p3,p4,k1,k2,k3;
    scanf("%d",&t);
    int T=t;
    while(t--){
        scanf("%d",&n);
        mem(head,-1);
        k=0;
        //加边信息
        for(int i=2;i<=n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&bac[i],&en[i]);
            bac[i]/=100;en[i]/=100;
        }
        //递归赋值ABC
        if(!dfs(1,1)||ab(1-A[1])<eps)printf("Case %d : impossible\n",T-t);
        else printf("Case %d : %.6f\n",T-t,(C[1]/(1-A[1])));
    }
	return 0;
}

HDU4405
https://vjudge.net/problem/HDU-4405

#include<bits/stdc++.h>
#include<cstdio>
#include<string>
#include<string.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const ll mod=998244353;
const int maxn=1e5;
ll jump[maxn+5];
double dp[maxn+5];
int main(){
   int n,m,a,b;//~取反
    while(~scanf("%d %d",&n,&m)&&(n||m)){
        mem(dp,0);
        mem(jump,-1);
        while(m--){
            scanf("%d %d",&a,&b);
            jump[a]=b;
        }
        for(int i=n-1;i>=0;i--){
            if(jump[i]<0){
                for(int j=1;j<=6;j++){
                    dp[i]+=(dp[i+j]+1.0);
                }
                dp[i]=dp[i]/6;
            }
            else dp[i]=dp[jump[i]];
            //cout<<dp[i]<<endl;
        }
        printf("%.4f\n",dp[0]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值