前一陣子FYH和XYY連講了兩週的矩陣乘法,不過單學一個矩乘頂多是在學模擬對吧,所以我們要知道這個算法怎麼應用,這裏的話主要講怎樣優化遞推。
首先,我們要知道怎樣操作矩乘,公式如下:
C[i,j]=∑a[i,k]*b[k,j]
前提是a的列數和b的行數一致。
而且我們還要知道,一切矩陣的0次方(即單位矩陣)是這個樣子的
衆所周知,一般來講不少遞推的遞推式是這樣的
F[i]=c[1]*f[i-1]+c[2]*f[i-2]+…+c[k]*f[i-k]+q
然後我們可以構造兩個矩陣來優化這一遞推式
A矩陣:
[c1,c2,c3,…,ck-1,ck,q
1 ,0 ,0,0,…,0,0,0
0 ,1 ,0,0,…,0,0,0
………………
0 ,0 ,0,0,…,1,0,0
0 ,0 ,0 ,0,…,0,0,1]
B矩陣
[fi-1
fi-2
fi-3
……
f0
1]
然後A*B=
[fi
fi-1
fi-2
……
f1
1]
當然如果想要推導第p項的話,我們可以得到這麼一條式子
Ap*B=
[fp
fp-1
fp-2
……
fp-k
1]
好了,然後我們來圍觀一下POJ的一道題3744
我們可以很輕易地知道踩在某一格子上的機率是這樣的
F[i]=p*f[i-1]+(1-p)*f[i-2]
然後再進行分段計算即可,分段的依據是地雷所在位置。
對於任何地雷所在位置i,跨過它的機率是f[i-1]*(1-p)
把這些機率相乘就得到結論
注意:
1. f[1]=1,f[0]=0(任何一段都是一樣的)
2. 把地雷按其所在位置排序後要先進行特判,如果起點就是地雷,掛;如果連續兩格是地雷,掛
3. 注意每一段的起點的取值
好吧,上代碼……
type matrix=array[1..2,1..2]of extended;
line=array[1..2]of extended;
var bas,a:matrix;
res:line;
ans:extended;
n,i,j:longint;
p,q:extended;
mine:array[1..100]of longint;
procedure sort(l,r: longint);//把地雷位置排序(不要問我爲什麼開快排,我當時有點無聊)
var
i,j,x,y: longint;
begin
i:=l;
j:=r;
x:=mine[(l+r) div 2];
repeat
while mine[i]<x do
inc(i);
while x<mine[j] do
dec(j);
if not(i>j) then
begin
y:=mine[i];
mine[i]:=mine[j];
mine[j]:=y;
inc(i);
j:=j-1;
end;
until i>j;
if l<j then
sort(l,j);
if i<r then
sort(i,r);
end;
function mul(a,b:matrix):matrix;//矩陣乘法
var t:matrix;i,j,k:longint;
begin
for i:=1 to 2 do
for j:=1 to 2 do
begin
t[i,j]:=0;
for k:=1 to 2 do
t[i,j]:=t[i,j]+a[i,k]*b[k,j];
end;
exit(t);
end;
function solve(a:line;b:matrix):line;//都是矩陣乘法,不過這裏操作的是隻有一列的矩陣
var t:line;i,j:longint;
begin
fillchar(t,sizeof(t),0);
for i:=1 to 2 do
for j:=1 to 2 do
t[j]:=t[j]+a[i]*b[i,j];
exit(t);
end;
function calc(n:longint):extended;//分段計算過程
var res:matrix;a:line;
begin
bas[1,1]:=p;
bas[1,2]:=q;
bas[2,1]:=1;
bas[2,2]:=0;
res[1,1]:=1;
res[1,2]:=0;
res[2,1]:=0;
res[2,2]:=1;//任何矩陣的零次方都是單位矩陣
a[1]:=0;
a[2]:=1;//f[1]=1,f[0]=0
dec(n);
while n>0 do
begin
if n mod 2=1 then res:=mul(bas,res);
n:=n shr 1;
bas:=mul(bas,bas);
end;
a:=solve(a,res);//a=a*bas^n
exit(a[1]*q);
end;
procedure main;
var last:longint;
begin
for i:=1 to n do
read(mine[i]);
readln;
sort(1,n);
if mine[1]=1 then
begin
writeln('0.0000000');
exit;
end;
for i:=2 to n do
if mine[i]-mine[i-1]=1 then
begin
writeln('0.0000000');
exit;
end;
ans:=1;
last:=1;
q:=1-p;
for i:=1 to n do
begin
ans:=ans*calc(mine[i]-last+1);
last:=mine[i]+1;
end;
writeln(ans:0:7);
end;
begin
while not eof do
begin
readln(n,p);
main;
end;
end.