2011年北京区域赛I-Activation

开始做的时候无从下手,这题就是一个概率dp。很多的细节。dp[i][j]指的是在有j个人的时候Tomato排的位置为j这个状态时的概率。这个题需要考虑的就是循环的问题,第一是p1的循环,所以对p2,p3,p4的概率进行了修改使他们的和为1.然后就是每次第一个人都失败,所以对于dp[m][n]的概率是每轮的概率和相加。还有一点就是dp[i][j]=dp[i+1][j+1]*p3+dp[i+1][j]*p2;因为是一个循环所以要对这个式子进行化简。先将最开始的值求出来。我先贴一个T掉的版本,这个版本更容易理解思路。

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
double dp[2010][2010];
double p(double a,int n){
 if(n==0)
  return 1.0;
 else{
  double cnt=p(a,n/2);
  if(n%2)
   return cnt*cnt*a;
  else
   return cnt*cnt;
 }
}
main(){
 double p1,p2,p3,p4;
 int n,m,k;
 while(scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)!=-1){
  p2=p2/(1-p1);p3=p3/(1-p1);p4=p4/(1-p1);
  memset(dp,0,sizeof(dp));
  dp[m][n]=1/(1-p(p2,n));
  for(int i=1;i<m;i++){
   dp[i][n]=dp[m][n]*p(p2,m-i);
  }
  for(int i=m+1;i<=n;i++){
   dp[i][n]=dp[m][n]*p(p2,n+m-i);
  }
  double ans=0;
  for(int j=n-1;j>=1;j--){
   double tmp=1-p(p2,j);
   for(int i=j;i>=1;i--){
    if(i==j){
     int c=i-1;
     for(int kk=0;kk<j;kk++){
      dp[i][j]+=dp[c%j+2][j+1]*p3*p(p2,kk);
      c++;
     }
     dp[i][j]=dp[i][j]/tmp;
 
    }
    else{
     if(!(i==1 && j==1)){
      dp[i][j]=(p2*(dp[i+1][j]*tmp-dp[i+1][j+1]*p3*(1-tmp)/p2)+dp[i+1][j+1]*p3)/tmp;
     }
     else{
      dp[1][1]=(dp[2][2]*p3)/(1-p2);
     }
    }
    if(i<=k)
     ans+=dp[i][j];
    //printf("%lf ",dp[i][j]);
   }
  // printf("\n");
  }
  for(int i=1;i<=k;i++){
   ans+=dp[i][n];
  }
  printf("%.5lf\n",ans*p4);
 }
}

后来通过变形,把这个式子改好了。

但是这道题还有一个坑就是p4==0的时候需要特判,因为这样我为此还WA了一次。

AC代码:

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
double dp[2010][2010];
double const eps=1e-5;
double p(double a,int n){
 if(n==0)
  return 1.0;
 else{
  double cnt=p(a,n/2);
  if(n%2)
   return cnt*cnt*a;
  else
   return cnt*cnt;
 }
}
main(){
 double p1,p2,p3,p4;
 int n,m,k;
 while(scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)!=-1){
  if(p4<eps){
    printf("0.00000\n");
    continue;
  }
  
  p2=p2/(1-p1);p3=p3/(1-p1);p4=p4/(1-p1);
  memset(dp,0,sizeof(dp));
  dp[m][n]=1/(1-p(p2,n));
  for(int i=1;i<m;i++){
   dp[i][n]=dp[m][n]*p(p2,m-i);
  }
  for(int i=m+1;i<=n;i++){
   dp[i][n]=dp[m][n]*p(p2,n+m-i);
  }
  double ans=0;double cnt=0;
  int c=n-2;
  for(int kk=0;kk<n-1;kk++){
   cnt+=dp[c%(n-1)+2][n]*p3*p(p2,kk);
    c++;
  }
  for(int j=n-1;j>=1;j--){
   double tmp=1-p(p2,j);
   for(int i=j;i>=1;i--){
    if(i==j){
     dp[i][j]=cnt/tmp;
     cnt=0;
     cnt+=dp[i][j]*p3;
    }
    else{
     if(!(i==1 && j==1)){
      dp[i][j]=(p2*(dp[i+1][j]*tmp-dp[i+1][j+1]*p3*(1-tmp)/p2)+dp[i+1][j+1]*p3)/tmp;
     }
     else{
      dp[1][1]=(dp[2][2]*p3)/(1-p2);
     }
    }
    if(i<=k)
     ans+=dp[i][j];
    if(i!=1 && i!=j)
     cnt+=dp[i][j]*p3*p(p2,i-1);
    //printf("%lf ",dp[i][j]);
   }
   //printf("\n");
  }
  for(int i=1;i<=k;i++){
   ans+=dp[i][n];
  }
  printf("%.5lf\n",ans*p4);
 }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值