概率dp小结

序:感觉概率什么的相比于其他的dp还是较为容易的 几乎都是线性的

解决概率问题的关键在于利用独立性对概率进行处理(乘乘加加)


A:

显然概率不能作为下标,那么就把钱数作为下标(由于总钱数<=10000)
然后贪心地取最小被抓概率
状态转移方程为 dp[i]=min(dp[i],dp[iV[i]]P[i]) P[i]为被抓概率,V[i]为收益

B:

这题要注意HP1和HP2要反着读。。。
预处理出A胜利(P1)和B胜利(P2)的概率
那么对于整个战局(忽略平局情况)A的胜率为Pa=P1/(P1+P2),B的胜率为Pb=P2/(P1+P2)
由于如果开两维内存会炸,所以要滚动数组
转移方程为 dp[cur][i]=dp[cur][i1]Pa+dp[cur1][i]Pb ;
//第一维为A的扣血量,第二维为B的扣血量

C:

这题有一个玄学贪心:根据Qi/Pi的值先从大到小排
然后就找到了状态转移的顺序:
ans+=dp[i][j1]Q[j];
dp[i][j]+=dp[i1][j1]P[j]+dp[i][j1](1P[j]Q[j])
第一维是消耗钱数,第二维是到第几扇门

D:

网上什么kmp,ac自动机…
其实暴力解就好了
这题最关键的思想是:虽然一串字符可以表示指定字符串s的多种状态[1,4] [1,8]…
但我们只用知道能表示的最长长度就好了
预处理表示到s的第几位时加上某个字符能表示的最长位数,记为A[i][j]
题目求包含s的概率,但我们并不方便直接求,可以通过计算不包含s的概率
即敲到第m位所表示s的长度不超过len
状态转移方程为 dp[i][A[j][k]]+=dp[i1][j]P[k] P[k]表示敲击k字母的概率

E:

这是这道题中为数不多不是线性的题目
显然是一个树形dp
在这题中,要能想到把之前的子树的信息合并,再与当前子树信息合并
我们只用记录到x点最长长度为l时概率,记为dp[x][l]
t为 1/(1+l)

void dfs(int x,int f){
    int cnt=0;
    for(int h=0;h<(int)edge[x].size();h++){
        int y=edge[x][h];
        if(y==f)continue;
        cnt++;
        dfs(y,x);
        if(cnt==1){
            for(int i=0;i<=S;i++)
                for(int j=0;j<=l;j++)
                    if(i+j<=S)dp[x][i+j]+=dp[y][i]*t;
            continue;
        }
        for(int i=0;i<=S;i++)
            for(int k=0;k<=S;k++)
                for(int j=0;j<=l;j++)
                    if(k+j+i<=S)dp1[x][max(k+j,i)]+=dp[x][i]*dp[y][k]*t;
        for(int i=0;i<=S;i++)dp[x][i]=dp1[x][i],dp1[x][i]=0;
    }
    if(cnt==0)dp[x][0]=1;
}

F:

不知道这里为什么会出现期望题
首先肯定要记录最远距离是什么
但我们可能会疑惑,万一定最右点为x,但整个过程中并走不到怎么办
所以可以用拓展的思想:dp[i][j][k]表示第i步到j这个点最远点为k且已到过k点
状态转移方程为:
dp[i][j][k]=dp[i-1][j][k]*(1-Pl-Pr)+dp[i-1][j][k-1]*Pr+dp[i-1][j][k+1]*Pl
[j< k]
dp[i][j][k]=dp[i-1][j][k]*(1-Pl-Pr)+dp[i-1][j-1][k-1]*Pr+dp[i-1][j-1][k]*Pr
初始化为dp[1][100][100]=1; 设100为起点

G:

这道题听说只能玄学卡常。。。

H:

这题的dp定义是当前自己的队伍种类对应的最大获胜概率
状态转移方程为:
dp[cur][i]=max(dp[cur][i],dp[cur^1][j]*P[j][i])
dp[cur][j]=max(dp[cur][j],dp[cur^1][j]*P[j][i])

I:

其实这题只要模拟就好了
类似于F题的dp状态定义,什么时刻,在哪个点,不经过哪个点

J:

又是一道令人作呕的博弈题
可以知道:
庄家去完之后,这一回合就结束了
所以可以直接得出庄家取完后的胜负情况
定义f2[i][j]为庄家取时我方有i张牌,对方有j张牌时的胜率
那么可以得出,当i<=j时我方是必输的,把f2[i][j]赋值为1
那么可以得到对于f2的状态转移方程为f2[i][j]=max(f2[i][j],f2[i][j+k]*/(double)13)[因为庄家足够聪明]
对于我方,有两种决策:1.抽牌,2.放弃抽牌,让庄家抽牌
对于第二种情况,胜率即为1-f2[i][j]
对于第一种情况,类似于f2的处理f1[i][j]=max(f1[i][j],f[i][j+k]*/(double)13)
代码实现:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define du double
du f2[50][50],f1[50][50];
void chk(){
    for(int i=21;i>=2;i--){
        for(int j=21;j>=2;j--){
            if(i<=j){
                f2[i][j]=1;
                continue; 
            }
            for(int k=1;k<=13;k++){
                int a=k;
                if(k>=10)a=10;
                f2[i][j]+=f2[i][j+a]/(du)13;
            }
        }
    }
}

void chk1(){
    for(int i=21;i>=2;i--){
        for(int j=21;j>=2;j--){
            for(int k=1;k<=13;k++){
                int a=k;
                if(k>=10)a=10;
                f1[i][j]+=f1[i+a][j]/(du)13;
            }
            f1[i][j]=max(f1[i][j],1-f2[i][j]);
        }
    }
}
int main(){
    chk();
    chk1();
    int T;
    scanf("%d",&T);
    while(T--){
        char A[5];
        scanf("%s",A);
        int a=0,b=0;
        for(int i=0;i<2;i++){
            if(A[i]=='T'||A[i]=='J'||A[i]=='K'||A[i]=='Q')a+=10;
            else if(A[i]=='A')a+=1;
            else a+=A[i]-'0';
        }
        for(int i=2;i<4;i++){
            if(A[i]=='T'||A[i]=='J'||A[i]=='K'||A[i]=='Q')b+=10;
            else if(A[i]=='A')b+=1;
            else b+=A[i]-'0';   
        }
        printf("%lf\n",f1[a][b]);
        if(f1[a][b]>0.5)puts("YES");
        else puts("NO");
    }
    return 0;
} 

K:

可以yy出游戏卡牌的排列就是 (n+m)!
看到n+m<=20,显然就是状压dp
当当前卡牌可以杀死敌人时,获胜的方案即为得到该状态的方案数* (n+mij)
状态转移方程即为 dp[i|(1<<k)]+=dp[i] 取第k张牌

int main(){
    int T;
    scanf("%d",&T);
    fact[0]=1;
    for(int i=1;i<=20;i++)fact[i]=fact[i-1]*i;
    while(T--){
        memset(dp,0,sizeof(dp));
        int p,n,m;
        scanf("%d%d%d",&p,&n,&m);
        int tta=n+m;
        for(int i=n;i<tta;i++)scanf("%d",&A[i]);
        long long cnt=0;
        dp[0]=1;
        int t=(1<<tta)-1;
        for(int i=0;i<=t;i++){
            if(!dp[i])continue;
            int cnt1=0,cnt2=0;
            int s=0;
            for(int k=0;k<tta;k++){
                if(i&(1<<k)){
                    if(k<n)cnt1++;
                    else cnt2++,s+=A[k];
                }
            }
            if(s>=p){
                cnt+=dp[i]*fact[tta-cnt1-cnt2];
                continue;
            }
            if(cnt1-cnt2+1<=0)continue;
            for(int k=0;k<tta;k++){
                if(i&(1<<k))continue;
                dp[i|(1<<k)]+=dp[i];
            }
        }
        long long g=gcd(cnt,fact[tta]); 
        printf("%lld/%lld\n",cnt/g,fact[tta]/g);
    } 
    return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
期望dp概率dp是两种不同的动态规划方法。 期望dp是指通过计算每个状态的期望值来求解最终的期望。在期望dp中,我们通常定义dp\[i\]表示在第i个状态时的期望值,然后通过状态转移方程来更新dp数组,最终得到最终状态的期望值。期望dp通常用于求解期望问题,例如求解骰子的期望点数、求解抽奖的期望次数等。 概率dp是指通过计算每个状态的概率来求解最终的概率。在概率dp中,我们通常定义dp\[i\]表示在第i个状态时的概率,然后通过状态转移方程来更新dp数组,最终得到最终状态的概率概率dp通常用于求解概率问题,例如求解抛硬币出现正面的概率、求解从一副牌中抽到红心的概率等。 总结来说,期望dp概率dp的区别在于它们所计算的是不同的值,期望dp计算的是期望值,而概率dp计算的是概率值。 #### 引用[.reference_title] - *1* [概率/期望dp专题](https://blog.csdn.net/qq_34416123/article/details/126585094)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【动态规划】数学期望/概率DP/期望DP详解](https://blog.csdn.net/weixin_45697774/article/details/104274160)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值