狂补概率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;
}