Probability

一些比较诡异的关于概率的DP

T1 IMT

呃,可以KMP求出Fail的位置。
但是懒得那么做..直接暴力 O(nlen2) 匹配求出矩阵。
然后矩阵乘或者直接求都可以了。

代码

这题调很久的一个严重原因就是不会输出‘%’
..要用两个‘%%’或是‘\%’才行。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define DB double
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define EPS 1e-6

#define M 14
int n,m,len;
char C[34],c[M];
string S,P,Tmp;
DB A[34],F[2][M],To[M][M];

int main(){
    while(scanf("%d %d",&n,&m)!=EOF){
        if(!n || !m)break;

        REP(i,0,n){
            scanf("%s %lf",&c,&A[i]);
            C[i]=c[0];
        }
        scanf("%s",c);
        S="";

        len=strlen(c);
        REP(i,0,len)S+=c[i];

        REP(i,0,M) REP(j,0,M) To[i][j]=0;

        P="";
        REP(i,0,len){
            REP(k,0,n){
                Tmp=P+C[k];
                DREP(j,i+1,-1){
                    if(j==0 || Tmp.substr(i-j+1,j)==S.substr(0,j)){
                        To[i][j]+=A[k];
                        break;
                    }
                }
            }
            P+=S[i];
        }
        bool f=0;
        DB Ans=0;

        REP(k,1,M)F[0][k]=0;
        F[0][0]=1;
        while(m--){
            REP(k,0,M)F[!f][k]=0;
            REP(j,0,len) REP(k,0,len+1)
                F[!f][k]+=F[f][j]*To[j][k];
            f^=1;
            Ans+=F[f][len];
        }

        printf("%.2lf%%\n",Ans*100.0);
    }
    return 0;
}

T2 R

给树上的每条边随机大小然后求直径小于S的树的概率。。
树形DP,合并子树的转移方法。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define DB double
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)

#define M 84
#define Mm 544
int T,n,L,S;
int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
    Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
DB F[M][Mm],Tmp[Mm],Val,Sum1[Mm],Sum2[Mm];
void DFS(int A,int f){
    int B,Son=0;
    F[A][0]=1;
    LREP(i,A)if((B=V[i])!=f){
        DFS(B,A);
        memset(Tmp,0,sizeof(Tmp));
        REP(j,0,L+1) REP(k,0,S-j+1)
            Tmp[k+j]+=Val*F[B][k];  

        Sum1[0]=F[A][0];
        REP(j,1,S+1)Sum1[j]=Sum1[j-1]+F[A][j];
        Sum2[0]=Tmp[0];
        REP(j,1,S+1)Sum2[j]=Sum2[j-1]+Tmp[j];

        REP(j,0,S+1){
            int ms=min(j,S-j);
            DB t=F[A][j]*Tmp[j];
            F[A][j]=F[A][j]*Sum2[ms]+Sum1[ms]*Tmp[j];
            if((j<<1)<=S)F[A][j]-=t;
        }
    }
}
int main(){
    scanf("%d",&T);
    REP(Case,1,T+1){
        scanf("%d %d %d",&n,&L,&S);
        memset(Head,tot=0,sizeof(Head));
        memset(F,0,sizeof(F));
        REP(i,1,n){
            int u,v;
            scanf("%d %d",&u,&v);
            Add_Edge(u,v);
            Add_Edge(v,u);
        }
        Val=1.0/(L+1);
        DFS(1,0);
        DB Ans=0;
        REP(i,0,S+1)Ans+=F[1][i];
        printf("Case %d: %.6lf\n",Case,Ans);
    }
    return 0;
}

T3 LPAD

如果 n105 的话是一道大水题、
然而 n109
其实还是一道大水题。
因为当n慢慢变大的时候概率的改变就可以忽略不计了。
也就是说当概率改变量减小到EPS以下时直接输出即可。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define DB double

#define M 1000004
#define EPS 1e-13
int n,m;
DB F[M];
int main(){
    while(scanf("%d %d",&m,&n)!=EOF){
        memset(F,0,sizeof(F));
        F[0]=1;F[1]=-1;
        DB Val=1.0/m;

        int i=0;

        while(i<=n){
            if(i){
                F[i]+=F[i-1];
                if(fabs(F[i]-F[i-1])<EPS)break;
            }
            if(i+1<M) F[i+1]+=F[i]*Val;
            if(i+m+1<M) F[i+m+1]-=F[i]*Val;
            i++;
        }

        printf("%.5lf\n",F[i-1]);
    }
    return 0;
}

T4 W

求图中长度为d的路径不经过某个点的概率。
原本应该直接写的,然而看错时间范围(15s)。
于是默默写了一遍矩阵乘….复杂度 O(n4logd)

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define DB double

#define M 54
int Case,n,m,d;
bool P[M][M];
struct Mat{
    DB V[M][M];
    void Clear(){memset(V,0,sizeof(V));}
    Mat friend operator *(Mat A,Mat B){
        Mat C;C.Clear();
        REP(i,1,n+1) REP(j,1,n+1) REP(k,1,n+1) C.V[i][j]+=A.V[i][k]*B.V[k][j]; 
        return C;
    }
}K,Res;

void Solve(int x){
    K.Clear();Res.Clear();
    REP(i,1,n+1)if(i!=x)Res.V[i][i]=1.0/n;
    REP(i,1,n+1)if(i!=x){
        int Tmp=0;
        REP(j,1,n+1) Tmp+=P[i][j];
        REP(j,1,n+1) if(j!=x && P[i][j]) K.V[i][j]=1.0/Tmp;
    }
    int p=d;
    while(p){
        if(p&1)Res=Res*K;
        K=K*K;p>>=1;
    }
    DB Ans=0;
    REP(i,1,n+1) REP(j,1,n+1)Ans+=Res.V[i][j];
    printf("%.10lf\n",Ans);
}
int main(){
    scanf("%d",&Case);
    while(Case--){
        scanf("%d %d %d",&n,&m,&d);
        memset(P,0,sizeof(P));
        REP(i,0,m){
            int u,v;
            scanf("%d %d",&u,&v);
            P[u][v]=P[v][u]=1;
        }
        REP(i,1,n+1)Solve(i);
    }
    return 0;
}

T5 BJ

先考虑庄家,此时玩家已经拿完牌。
那么庄家只能继续拿牌直到他的点数≥玩家的点数。
定义这个概率为 Gi,j ,即庄家 i 点玩家j点的使玩家不获胜概率。
那么玩家的概率反着求一下即可。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define DB double

#define M 14
#define N 34
int Test;
DB F[M];
char C[M];
map<char,int>V;
DB G[N][N],DP[N][N];
int main(){
    REP(i,1,10)F[i]=1.0/13;
    F[10]=4.0/13;

    V['A']=1;
    V['2']=2,V['3']=3,V['4']=4,V['5']=5;
    V['6']=6,V['7']=7,V['8']=8,V['9']=9;
    V['T']=V['J']=V['Q']=V['K']=10;

    REP(i,2,22)
        DREP(j,21,1)
            if(j>=i)G[i][j]=1;
            else REP(t,1,11)G[i][j]+=G[i][j+t]*F[t];

    DREP(a,21,1)
        REP(b,2,22){
            DB Tmp=0;
            REP(t,1,11)Tmp+=DP[a+t][b]*F[t];
            DP[a][b]=max(1-G[a][b],Tmp);
        }

    scanf("%d",&Test);
    while(Test--){
        scanf("%s",C);
        int a,b;a=b=0;
        REP(i,0,2)a+=V[C[i]];
        REP(i,2,4)b+=V[C[i]];
        puts(DP[a][b]>=0.5?"YES":"NO");
    }
    return 0;
}

T6 H

写法十分奇怪。
网上怎么全是状压DP啊?
这里通过..算了自行看注释吧。
Fi,j,k 为抽了i张牌后,
还有j次抽牌机会,
抽到了k张伤害牌的概率(分数表示)
然后复杂度是 O((n+m)nm+pm2) 的,似乎比 O(2m) 要小。
嗯,这个还能写更大一点的数据范围。

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

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define DB double
#define LL long long
#define M 24
#define Mm 1004

int T;
int p,n,m,atk[M];
LL Gcd(LL a,LL b){return b?Gcd(b,a%b):a;}
struct Node{
    LL a,b;
    Node Easy(){LL d=Gcd(a,b);a/=d,b/=d;return *this;}
    Node operator +(const Node &_){return ((Node){a*_.b+b*_.a,b*_.b}).Easy();}
    Node operator *(const Node &_){return ((Node){a*_.a,b*_.b}).Easy();}
    void Add(const Node &_){(*this)=(*this)+_;}
};//就是分数结构体
Node F[M][M][M];
int G[M][Mm];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d %d",&p,&n,&m);
        REP(i,0,m) scanf("%d",&atk[i]);

        REP(i,0,M) REP(j,0,M) REP(k,0,M) F[i][j][k]=(Node){0,1};

        F[0][1][0]=(Node){1,1};
        REP(i,0,n+m) REP(j,1,n+2) REP(k,0,m+1){
            Node Val=F[i][j][k]; if(Val.a==0)continue;
            F[i+1][j-1][k+1].Add(Val*(Node){m-k,n+m-i});
            F[i+1][j+1][k].Add(Val*(Node){n-i+k,n+m-i});
        }

        memset(G,0,sizeof(G));
        G[0][0]=1;
        REP(v,0,m){
            DREP(i,m-1,-1)
                REP(j,0,p+1)G[i+1][min(p,j+atk[v])]+=G[i][j];
            //G 取i张牌造成j点伤害的方案数。 
        }
        Node Ans=(Node){0,1};
        REP(i,1,m+1){
            Node Tmp=(Node){G[i][p],1};
            REP(j,0,i) Tmp=Tmp*(Node){1,m-j};
            REP(j,1,i+1) Tmp=Tmp*(Node){j,1};
            //Tmp为取i张B类牌打死敌人的概率
            REP(j,1,n+m+1)if(F[j][0][i].a)Ans.Add(F[j][0][i]*Tmp);
            REP(j,1,n+2)if(F[n+m][j][i].a)Ans.Add(F[n+m][j][i]*Tmp);
        }

        printf("%d",Ans.a);putchar('/');
        printf("%d\n",Ans.b);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值