poj3744 概率dp + 矩阵快速幂

本文介绍了一种使用动态规划和矩阵乘法计算在一条布满地雷的路上,从起点出发到达安全位置的概率的方法。通过将路径划分为多个区间并分别计算每个区间的通过概率,最终得到整体的通过概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710586.html

题意:在一条不满地雷的路上,你现在的起点在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.对于后面的段我们也是一样的假设,这样就乘起来就是答案了。
 
对于每一段的概率的求法可以通过矩阵乘法快速求出来。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
using namespace std;
const int maxn = 100 + 10;
#define INF 0x3f3f3f3f
#define clr(x,y) memset(x,y,sizeof x )
typedef long long ll;
#define eps 10e-10
const int Mod = 500 + 7;
typedef pair<int, int> P;

double p;
int x[20];
struct Matrix
{
	Matrix()
	{
		maze[0][0] = maze[1][1] = 1;
		maze[0][1] = maze[1][0] = 0;
	}
	Matrix(double x,double y,double xs,double ys)
	{
		maze[0][0] = x;
		maze[0][1] = y;
		maze[1][0] = xs;
		maze[1][1] = ys;
	}
	void show()
	{
		for(int i = 0; i < 2; i ++)
		{
			for(int j = 0; j < 2; j ++)
			{
				cout << maze[i][j] << " ";
			}
			cout << endl;
		}
	}
	inline Matrix operator * (const Matrix & b)const
	{
		Matrix c;
		for(int i = 0; i < 2; i ++)
		{
			for(int j = 0; j < 2; j ++)
			{
				c.maze[i][j] = 0;
				for(int k = 0; k < 2; k ++)
				{
					c.maze[i][j] += maze[i][k] * b.maze[k][j];
				}
			}
		}
		return c;
	}
	double maze[2][2];
};
Matrix Matrix_pow(int n)
{
	Matrix ans;
	Matrix t(p,1-p,1,0);
//	t.show();
//	cout << "Pass _ by" << endl;
//	cout << n << endl;
	while(n)
	{
		if(n & 1)
		{
			ans = ans * t;
		}
		n >>= 1;
		t = t * t;
	}
//	ans.show();
	return ans;
}
double solve(int len)
{
	Matrix ret = Matrix_pow(len - 1);
//	ret.show();
	return 1 - ret.maze[0][0];
}
int main()
{
	int n;
	while( ~ scanf("%d%lf",&n,&p))
	{
		for(int i = 1; i <= n; i ++)
		scanf("%d",&x[i]);
		sort(x + 1,x+n+1);
		x[0] = 0;
		double ans = 1;
		for(int i = 1; i <= n; i ++)
		{
		    if(x[i] - x[i - 1] == 0)
            {
                continue;
            }
			ans *= solve(x[i] - x[i - 1]);
		}
		printf("%.7f\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值