概率DP问题整理(一)

第一题:Hdu 3853 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3853

简单概率DP题。注意思维逆过来考虑。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

#define Maxn 1005
struct Probab
{
   double same,right,down;
}probab[Maxn][Maxn];

double dp[Maxn][Maxn];
int main()
{
   #ifndef ONLINE_JUDGE
      freopen("in.txt","r",stdin);
   #endif
   int r,c;
   while(scanf(" %d %d",&r,&c)!=EOF)
   {
      for(int i=1;i<=r;i++)
      {
         for(int j=1;j<=c;j++)
         {
            scanf(" %lf %lf %lf",&probab[i][j].same,&probab[i][j].right,&probab[i][j].down);         
         }
      }
      memset(dp,0,sizeof(dp));
      for(int i=r;i>=1;i--)
      {
         for(int j=c;j>=1;j--)
         {
            if(j==c && i==r) continue;
            if(fabs(probab[i][j].same-1.0)<1e-8) dp[i][j] += 2;
            else dp[i][j] = (dp[i][j+1]*probab[i][j].right + dp[i+1][j]*probab[i][j].down + 2)/(1-probab[i][j].same);
         }
      }
      printf("%.3lf\n",dp[1][1]);
   }
   return 0;
}
第二题:Hdu 4336

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4336

状态压缩+概率DP。

状态转移方程:

dp[s] = ps*dp[s]+sigma(  p[i]*dp[ s ^ (1<<i) ] ) +1, (0<=i<n,假设下标从1到n)
ps = 1 - sigma( p[i] )

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

#define Maxn 21

double probab[Maxn];

double dp[1<<Maxn];
int main()
{
   #ifndef ONLINE_JUDGE
      freopen("in.txt","r",stdin);
   #endif
   int n;
   while(scanf(" %d",&n)!=EOF)
   {
      for(int i=1;i<=n;i++)
      {
         scanf(" %lf",&probab[i]);
      }
      memset(dp,0,sizeof(dp));
      dp[0] = 0;
      for(int i=1;i<(1<<n);i++)
      {
         double temp = 0;
         double total = 0;
         for(int j=1;j<=n;j++)
         {
            if(i & 1<<(j-1))
            {
               temp += probab[j] * dp[i^(1<<(j-1))];
               total += probab[j];
            }
         }
         temp ++;
         dp[i] = temp/total;
      }
      printf("%lf\n",dp[(1<<n)-1]);
   }
   return 0;
}

第三题:Poj 3744 Scout YYF I

题目链接:http://poj.org/problem?id=3744

思路摘自其他人:


题意:在一条不满地雷的路上,你现在的起点在1处。在N个点处布有地雷,1<=N<=10。地雷点的坐标范围:[1,100000000].
每次前进p的概率前进一步,1-p的概率前进1-p步。问顺利通过这条路的概率。就是不要走到有地雷的地方。
 
设dp[i]表示到达i点的概率,则 初始值 dp[1]=1.
很容易想到转移方程: dp[i]=p*dp[i-1]+(1-p)*dp[i-2];
但是由于坐标的范围很大,直接这样求是不行的,而且当中的某些点还存在地雷。
 
N个有地雷的点的坐标为 x[1],x[2],x[3]```````x[N].
我们把道路分成N段:
1~x[1];
x[1]+1~x[2];
x[2]+1~x[3];
`
`
`
x[N-1]+1~x[N].
 
这样每一段只有一个地雷。我们只要求得通过每一段的概率。乘法原理相乘就是答案。
对于每一段,通过该段的概率等于1-踩到该段终点的地雷的概率。
 
就比如第一段 1~x[1].  通过该段其实就相当于是到达x[1]+1点。那么p[x[1]+1]=1-p[x[1]].
但是这个前提是p[1]=1,即起点的概率等于1.对于后面的段我们也是一样的假设,这样就乘起来就是答案了。
 
对于每一段的概率的求法可以通过矩阵乘法快速求出来。

另外注意:关于double,scanf要用%lf,printf只能用%f才对。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

#define Maxn 12
#define MATRIX_SIZE 2
int place[Maxn];

struct Matrix
{
   double elem[MATRIX_SIZE][MATRIX_SIZE];
   int size;
   Matrix(){memset(elem,0,sizeof(elem));}
   void setSize(int _size)
   {
      size = _size;
   }
   Matrix operator = (const Matrix & other)
   {
      setSize(other.size);
      for(int i=0;i<size;i++)
      {
         for(int j= 0;j<size;j++)
         {
            elem[i][j] = other.elem[i][j];
         }
      }
      return *this;
   }
   Matrix operator * (const Matrix & other)
   {
      Matrix temp;
      temp.setSize(size);
      for(int i=0;i<size;i++)
      {
         for(int j=0;j<size;j++)
         {
            for(int k=0;k<size;k++)
            {
               temp.elem[i][j] += elem[i][k] * other.elem[k][j];
            }
         }
      }
      return temp;
   }
   void Power(int exp)
   {
      Matrix E;
      E.setSize(size);
      for(int i=0;i<size;i++) E.elem[i][i] = 1;
      while(exp)
      {
         if(exp & 1) E = E * (*this);
         *this = (*this) * (*this);
         exp >>= 1;
      }
      *this = E;
   }
};
Matrix m;
void init(double p)
{
   m.setSize(2);
   m.elem[0][0] = 0.0;
   m.elem[0][1] = 1.0;
   m.elem[1][0] = 1-p;
   m.elem[1][1] = p;
}

int main()
{
   #ifndef ONLINE_JUDGE
      freopen("in.txt","r",stdin);
   #endif
   int n;
   double p;
   while(scanf(" %d %lf",&n,&p)!=EOF)
   {
      place[0] = 0;
      for(int i=1;i<=n;i++)
      {
         scanf(" %d",&place[i]);
      }
      sort(place+1,place+1+n);
      double ans = 1;
      for(int i=1;i<=n;i++)
      {
         init(p);
         if(place[i] == place[i-1]) continue;
         int t = place[i] - place[i-1];
         m.Power(t-1);
         ans *= (1 - m.elem[1][1]);
      }
      printf("%.7f\n",ans );
   }
   return 0;
}

第四题:Uva 11722 Joining with Friend

题目连接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2769

连续概率DP:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

const double eps = 1e-8;  
  
struct Point  
{  
    int x;  
    int y;  
    Point() {}  
    Point(int _x,int _y):x(_x),y(_y) {}  
    friend Point operator + (Point a,Point b)  
    {  
        return Point(a.x+b.x , a.y+b.y);  
    }  
    friend Point operator - (Point a,Point b)  
    {  
        return Point(a.x-b.x , a.y-b.y);  
    }  
}; 

int dcmp(double x)  //三态函数  
{  
    if(fabs(x)<eps)//在一定的精度范围内可认为是0  
        return 0;  
    return x>0?1:-1;  
}  
double det(Point a,Point b)  // 叉积,重载叉积函数  
{  
    return a.x*b.y-a.y*b.x;  
}  
double det(Point a,Point b,Point o)  // 叉积  
{  
    return det(a-o,b-o);
}  
double det(Point a,Point b,Point c,Point d)  // 叉积  
{  
    return det(b-a,d-c);  
}
//A,B,C,D是矩形的四个点
Point A,B,C,D;

//p1,p2是y=x-w线段上的任意两个点
//p3,p4是y=x+w线段上的任意两个点
Point p1,p2,p3,p4;

int t1, t2, s1, s2, w;

double init()
{
   A.x = t1;
   A.y = s1;
   B.x = t2;
   B.y = s1;
   C.x = t2;
   C.y = s2;
   D.x = t1;
   D.y = s2;
   p1.x = 1;
   p1.y = 1-w;
   p2.x = 2;
   p2.y = 2-w;
   p3.x = 1;
   p3.y = 1+w;
   p4.x = 2;
   p4.y = 2+w;
   //>0点在线上方,<0点在线下方
   //处理下方的线段
   int bP = dcmp(det(p1,p2,B));
   int aP = dcmp(det(p1,p2,A));
   int cP = dcmp(det(p1,p2,C));
   int dP = dcmp(det(p1,p2,D));
   double area1 = 0;
   if(bP>=0) area1 = 0;
   else if(bP<0 && aP>=0 && cP>=0)
   {
      area1 = fabs((s1+w-t2)*(t2-w-s1)) * 0.5;
   }
   else if(bP<0 && aP<=0 && cP<=0 && dP>=0)
   {
      area1 = fabs((t2-t1)*(s2-s1)) - fabs((t1-w-s2)*(s2+w-t1)) * 0.5;
   }
   else if(bP<0 && cP<0 && aP>0 && dP>0)
   {
      area1 = (fabs(s1+w-t2) + fabs(s2+w-t2)) *fabs(s2-s1) * 0.5;
   }
   else if(bP<0 && aP<0 && cP>0 && dP>0)
   {
      area1 = (fabs(t1-w-s1) + fabs(t2-w-s1))*fabs(t2-t1)*0.5;
   }
   else
   {
      area1 = fabs((t2-t1)*(s2-s1));
   }
   //处理上方的线段
   bP = dcmp(det(p3,p4,B));
   aP = dcmp(det(p3,p4,A));
   cP = dcmp(det(p3,p4,C));
   dP = dcmp(det(p3,p4,D));
   double area2 = 0;
   if(bP>=0) area2 = 0;
   else if(bP<0 && aP>=0 && cP>=0)
   {
      area2 = fabs((s1-w-t2)*(t2+w-s1)) * 0.5;
   }
   else if(bP<0 && aP<=0 && cP<=0 && dP>=0)
   {
      area2 = fabs((t2-t1)*(s2-s1)) - fabs((t1+w-s2)*(s2-w-t1)) * 0.5;
   }
   else if(bP<0 && cP<0 && aP>0 && dP>0)
   {
      area2 = (fabs(s1-w-t2) + fabs(s2-w-t2)) *fabs(s2-s1) * 0.5;
   }
   else if(bP<0 && aP<0 && cP>0 && dP>0)
   {
      area2 = (fabs(t1+w-s1) + fabs(t2+w-s1))*fabs(t2-t1)*0.5;
   }
   else
   {
      area2 = fabs((t2-t1)*(s2-s1));
   }
   return (area2 - area1)/(t2-t1)/(s2-s1);
}
int main()
{
   #ifndef ONLINE_JUDGE
      freopen("in.txt","r",stdin);
   #endif
   int t;
   int cas = 0;
   scanf(" %d",&t);
   while(t--)
   {
      cas++;
      scanf(" %d %d %d %d %d",&t1,&t2,&s1,&s2,&w);
      double ans = init();
      printf("Case #%d: %.7f\n",cas,ans);
   }
   return 0;
}


第五题:Uva 11762 - Race to 1

题目连接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2862

质数分解转换。

设:sum[x]表示1到x中有多少个素数,p[x]表示x的素数因子个数,不难发现递推关系如下:
dp(x) = 1 + dp(x) * (sum[x] - p[x])/sum[x] + sigma(dp(x/yi)) /sum[x];其中yi是x的素因子。

两边同时乘sum[x].我们得到:

p[x] * dp(x) = sum[x] + sigma(dp(x/yi))

dp(x)即可求出。

注意用筛法求质数同时预处理出sum[x] 和p[x].

求筛法的时候要注意范围和系数。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

#define Maxn 1000002
int n;
int notPrime[Maxn+5];
vector <int> primeFactor[Maxn+5];
int primeSum[Maxn+5];
double dp[Maxn+5];

void init()
{
   memset(notPrime,0,sizeof(notPrime));
   notPrime[1] = 1;
   for(int i=2;i<=Maxn;i++)
   {
      if(!notPrime[i])
      {
         for(int j=i*2;j<=Maxn;j+=i)
         {
            notPrime[j] = 1;
            primeFactor[j].push_back(i);
         }
         primeFactor[i].push_back(i);
      }
   }
   memset(primeSum,0,sizeof(primeSum));
   for(int i=2;i<=Maxn;i++)
   {
      primeSum[i] += primeSum[i-1] + (notPrime[i] == 0);
   }
   memset(dp,0,sizeof(dp));
}

double solve(int n)
{
   if(n == 1) return 0;
   if(dp[n]!=0) return dp[n];
   double ans = 0;
   for(int i=2;i<=n;i++)
   {
      ans = 0;
      for(int j=0;j<primeFactor[i].size();j++)
      {
         ans += dp[i/primeFactor[i][j]];
      }
      ans = ans + primeSum[i];
      ans = ans / primeFactor[i].size();
      dp[i] = ans;
   }
   return dp[n];
}
int main()
{
   #ifndef ONLINE_JUDGE
      freopen("in.txt","r",stdin);
   #endif
   int t;
   int cas = 0;
   init();
   scanf(" %d",&t);
   while(t--)
   {
      cas++;
      scanf(" %d",&n);
      double ans = solve(n);
      printf("Case %d: %.7f\n",cas,ans);
   }
   return 0;
}


你可能感兴趣的:(概率DP问题整理(一))