[POJ 3744] Scout YYF I 題解
[POJ 3744] Scout YYF I 題解
題意:
在一條有地雷的路上,你現在的起點在 \(1\) 處。在 \(n\) 個點處布有地雷。每次有 \(p\) 的概率前進一步,\(1−p\) 的概率前進 \(2\) 步。問順利通過這條路的概率。多組資料。
資料範圍:\(1 \le n \le 10\) ,\(0.25 \le p \le 0.75\) 地雷點的座標範圍:$ [1,10^8] $,答案保留七位小數。
題解:
\(~~~~\) 顯然概率DP,設:\(dp_i\) 表示道路 \([1,i]\) 安全通過的概率 。
\(~~~~\) 則有轉移方程 \(dp_i=dp_{i-1} \times p + dp_{i-2} \times (1-p)\)
\(~~~~\) 直接轉移,時間 \(\mathcal{O(T\times 10^8)}\) ,發現時空都在 \(10^8\) 級別。
\(~~~~\) 甚至多組資料,直接狗帶。
\(~~~~\) 因此要考慮優化。
優化1:矩陣快速冪
\(~~~~\) 發現轉移是一維的,且最多與前兩項有關。
\(~~~~\) 構造矩陣
\[\begin{bmatrix} p & 1-p \\ 1 & 0 \end{bmatrix}\quad \]
\(~~~~\) 分段對空白部分進行矩陣快速冪即可。
\(~~~~\) 時間 \(\mathcal{O(T \log 10^8)}\)
\(~~~~\) 程式碼:沒有 不展示
優化2:數學/找規律/亂搞
\(~~~~\) 由上可知,重點在於有很多的空白部分。
\(~~~~\) 但其實空白部分在經過約 \(100\) 次DP之後數值就幾乎不會變化(即變化量遠遠小於 \(10^7\) )。
\(~~~~\) 以上的結論可以打表發現( \(p=0.5\) ):
\(~~~~\) 其實在 \(50\) 左右數值已經穩定,但考慮不可見部分可能相差過小,為穩妥我們可以將區間之間間隔 \(>100\) 的空白區域全部縮短成長度為 \(100\) 的空白區間,可以穩過。
\(~~~~\) 說白了就是可以卡精度。
\(~~~~\) 時間:\(\mathcal{O(T 100\ n)}\)
\(~~~~\) 程式碼:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int arr[25];
bool ma[5005];//儲存是否是雷
double dp[5005];
int main() {
int n;
double p;
while(~scanf("%d %lf",&n,&p))
{
bool flag=true;
for(int i=1;i<=n;i++) scanf("%d",&arr[i]);
sort(arr+1,arr+1+n);
for(int i=0;i<n;i++)
{
if(arr[i+1]-arr[i]>100)
{
for(int j=n;j>i;j--) arr[j]-=(arr[i+1]-arr[i]-100);
}
}
dp[1]=1;
if(!flag) continue;
for(int i=1;i<=n;i++) ma[arr[i]]=true;
for(int i=0;i<=arr[n];i++)
{
if(!ma[i])
{
dp[min(i+1,arr[n]+1)]+=dp[i]*p;
dp[min(i+2,arr[n]+1)]+=dp[i]*(1-p);
}
else ma[i]=false;//隨時清零,省掉 memset
dp[i]=0;//同上
}
printf("%.7f\n",dp[arr[n]+1]);
for(int i=arr[n]-2;i<=arr[n]+2;i++) dp[i]=0;//最後需要清掉
}
return 0;
}