1. 程式人生 > >ICPC-概率DP-ZOJ3329(概率DP+解未知數) POJ3744(概率DP+矩陣快速冪) HDU4089 HDU4035 HDU

ICPC-概率DP-ZOJ3329(概率DP+解未知數) POJ3744(概率DP+矩陣快速冪) HDU4089 HDU4035 HDU

這題我先留個坑。晚點補題解
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;
}

#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;
}
#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;
}