1. 程式人生 > >poj-3744 Scout YYF I [用矩陣優化概率遞推式]

poj-3744 Scout YYF I [用矩陣優化概率遞推式]

/*
題意:在一條不滿地雷的路上,你現在的起點在1處。
在N個點處布有地雷,1<=N<=10。地雷點的座標範圍:[1,100000000].
每次前進p的概率前進一步,1-p的概率前進兩步。
問順利通過這條路的概率。就是不要走到有地雷的地方。

dp[i] 表示走到i的概率
dp[i+1]=dp[i]*p+dp[i-1]*(1-p); 

用矩陣快速冪優化
遞推式轉矩陣 
| 0    1| |dp(k-1)|  = |  dp(k) | 
| 1-p  p| | dp(k) |    | dp(k+1)| 
dp[0]=0,dp[1]=1;
設x[i] 表示第i個地雷的位置
把道路分成開
1~x[1] x[1]+1~x[2]...... x[n-1]+1~x[n]
對於每一段  sus[i] 表示 x[i-1]+1~x[i]-1這一段沒有掛掉
那麼sus[i]= 1-從x[i-1]+1剛好走到x[i]的概率;
那麼 ans為sus[i]的總乘積(即每一段都沒屎) 
*/ 
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
typedef vector<double> vec;
typedef vector<vec> mat;
const int maxn=30;
const double eps=1E-8; 
int x[maxn];
int n;
double p;
mat mul(const mat &a,const mat &b)
{
	mat c(2,vec(2,0));
	for(int i=0;i<2;++i)
		for(int j=0;j<2;++j)
			for(int k=0;k<2;++k)
				c[i][j]+=a[i][k]*b[k][j];
	return c;
}
double T(int k)
{
	double s[2][2]={{0,1},{1-p,p}};
	mat A(2,vec(2));
	mat p(2,vec(2,0));
	p[0][0]=p[1][1]=1;
	for(int i=0;i<2;++i)
		for(int j=0;j<2;++j)
			A[i][j]=s[i][j];
	while(k>0)
	{
		if(k&1)
			p=mul(A,p);
		k=k>>1;
		A=mul(A,A);
	}
	return  p[1][1];
}
void DP()
{
	double ans=1;
	double tmp=T(x[1]-1);
	ans*=(1-tmp);
	for(int i=2;i<=n;++i)
	{
		if(x[i]==x[i-1]) continue;
		tmp=1-T(x[i]-x[i-1]-1);
		ans*=tmp;
	}
	printf("%.7f\n",ans<eps?0:ans);
}
int main()
{
	while(~scanf("%d %lf",&n,&p))
	{
		for(int i=1;i<=n;++i)
			scanf("%d",x+i);
		sort(x+1,x+1+n);
		DP();
	}
	return 0;
}