第一题: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; }
题目连接: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; }