ICPC-概率DP-ZOJ3329(概率DP+解未知數) POJ3744(概率DP+矩陣快速冪) HDU4089 HDU4035 HDU
阿新 • • 發佈:2019-02-10
這題我先留個坑。晚點補題解
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;
}