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