概率DP问题整理(一)

来源:互联网 发布:快递数据 编辑:程序博客网 时间:2024/05/01 23:15

第一题: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 1005struct 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 21double 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 2int 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 1000002int 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;}


原创粉丝点击