题目链接:
http://poj.org/problem?id=3744
题目大意:
一个人,一开始处于位置1,在n个位置上有地雷,不能接触,每次人有
p
概率向前走一步,有
分析:
dp[i]表示走到i处的概率的话,可以很快推出
dp[i]=p∗dp[i−1]+(1−p)∗dp[i−2]
即 dp[i] 是由 dp[i−1] 与 dp[i−2] 贡献得到,那么从1开始,向上不断贡献,遇到地雷位置跳过,然后走到 a[n]+1 位置的概率即是答案,但是根据题意 a[n]+1 过大,且有多组数据,所有需要优化。
可以发现在每段中,情况几乎都相同,而且dp转移式很自然的能想到斐波那契 F[i]=F[i−1]+F[i−2] ,所以这题可以用矩阵快速幂计算每一段的概率值,下一段的起点即是之前一个地雷的下一个位置,分别计算不走到各地雷的概率并作积即可
注意:
- 地雷位置可以为1,即起点
- 地雷读入为乱序
- 地雷可能有重合
- 地雷之间可能连续,即出现2,3连续位置的地雷
代码:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int maxn = 207;
const int mod = 1000000007;
const int Mod = 1000;
struct mat{
int r,c;
double m[10][10];
mat(){}
mat(int _r,int _c):r(_r),c(_c){};
};
void init(mat &a){
memset(a.m,0,sizeof(a.m));
}
mat mul(mat a, mat b){
mat tmp(a.r,b.c);
for (int i = 1 ; i <= tmp.r; i ++)
{
for (int j = 1 ; j <= tmp.c ;j ++){
tmp.m[i][j] = 0;
for (int k =1 ; k <= a.c ; k ++){
tmp.m[i][j] = (tmp.m[i][j]+(a.m[i][k]*b.m[k][j]));
}
}
}
return tmp;
}
mat QP(mat a ,int n){
mat ans(a.r,a.r),tmp(a.r,a.r);
memcpy(tmp.m,a.m,sizeof(tmp.m));
init(ans);
for (int i = 1 ; i <= ans.r ; i ++)
{
ans.m[i][i] = 1;
}
while (n){
if (n&1) ans = mul(ans,tmp);
n >>= 1;
tmp = mul(tmp,tmp);
}
return ans;
}
void print(mat a){
for (int i = 1 ; i <= a.r ;++ i){
for (int j = 1 ; j <= a.c ; ++j){
printf("%f",a.m[i][j]);
if (j==a.c) putchar('\n');
else putchar(' ');
}
}
}
int n;
double p;
int arr[maxn];
double dp[100000009];
int main()
{
while (~scanf("%d%lf",&n,&p))
{
mat a(2,2);
a.m[1][1] = p;
a.m[1][2] = 1;
a.m[2][1] = 1-p;
a.m[2][2] = 0;
for (int i =1 ; i <= n ; i ++)
scanf("%d",&arr[i]);
sort(arr+1,arr+n+1);
if (arr[1] == 1)
printf("%.7f\n",0);
else
{
double t = 1;
mat ans = QP(a,arr[1]-1);
t *= (1-ans.m[1][1]);
int f = 1;
for (int i = 2 ; i <= n ; i ++)
{
if (arr[i]==arr[i-1])
continue;
if (arr[i]-arr[i-1]==1)
{
f = 0;
printf("%.7f\n",0);
break;
}
ans = QP(a,arr[i]-arr[i-1]-1);
t *= (1-ans.m[1][1]);
}
if (f)
printf("%.7f\n",t);
}
}
return 0;
}