1. 程式人生 > 其它 >【dp選講】淺談概率dp

【dp選講】淺談概率dp

【dp選講】概率與期望

說在前面

如果您此前還沒有了解過任何有關概率的內容的話,請先自行學習概率和期望的相關定理,然後再食用本部落格。

概率選題

注:概率和期望一般都是計算題,所以本部落格僅從解題的角度向您展示概率和期望,具體做題的時候還是有一點思考難度的,但是一般比較統一。

CF148D

題目大意:有兩個人往袋子裡面抓老鼠,老鼠分為兩類,一類是白老鼠,一類是黑老鼠,獲勝規則是誰先抓到白老鼠誰就獲勝。A抓老鼠的時候會附帶著有一隻老鼠溜出袋子,而B不會。如果最後是A抓到白老鼠或老鼠都跑光了,A也獲勝,請問B獲勝的概率是多少。輸入:N只白老鼠和M只黑老鼠。

分析:

設 $$dp[i][j]$$ 為袋子裡面有 i 只白老鼠和 j 只黑老鼠時的輪到B的獲勝概率。

初始化:$$dp[0][j]=0$$ ,$$dp[i][0]=1$$,意思分別是:如果一隻白老鼠都沒有的話,就必輸無疑;如果沒有黑老鼠,反而全是白老鼠的話,那就必勝無疑。

先考慮B在當前狀態下一次就抓到白老鼠的歐皇行為:

\[概率為:\frac{i}{i+j} \]

接著思考B拿到黑老鼠,而A拿到白老鼠的非酋行為:

\[概率為:\frac{j}{i+j}*\frac{i}{i+j-1} \]

【注:這是個分佈事件,第一步是B非酋拿黑擺爛,第二步才是A歐皇拿白。】

第三種:B抓到黑老鼠,A也抓到黑老鼠,最後又跑出一隻黑老鼠,此時的概率是:

\[\frac{j}{i+j}*\frac{j-1}{i+j-1}*\frac{j-2}{i+j-2} \]

上面這種情況可以轉移到\(dp[i][j-3]\)

.

第四種:依舊是兩人抓到黑老鼠,但是跑出來的是白老鼠,此時的概率為:

\[\frac{j}{i+j}*\frac{j-1}{i+j-1}*\frac{i}{i+j-2} \]

可轉移到\(dp[i-1][j-2]\).

根據以上式子,dp轉移即可。

#include<iostream>
#include<cstdio>
#define Maxn 5000 
using namespace std;
typedef double db;
int n,m;
db dp[Maxn][Maxn];
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) dp[i][0]=1;
	for(int j=1;j<=m;j++) dp[0][j]=0;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			dp[i][j]+=(db)i/(i+j);
			if(j>=3) 
				dp[i][j]+=(db)j/(i+j)*(j-1)/(i+j-1)*(j-2)/(i+j-2)*dp[i][j-3];
			if(j>=2) 
				dp[i][j]+=(db)j/(i+j)*(j-1)/(i+j-1)*i/(i+j-2)*dp[i-1][j-2];
		}
	}
	printf("%.9lf",dp[n][m]);
	return 0;
}  

[Scout YYF I][http://poj.org/problem?id=3744]

題目大意:在一個數軸上,有n個雷。一開始,你在原點,現在,你有p的概率走一步,有(1-p)的概率走兩步,請問你有多大的概率可以安全通過雷陣?

分析:

直接算概率是不好求的,我們不如利用容斥原理,求出我們必定踩雷的概率(真非酋才能完成的壯舉),最後用1減去即可。

於是乎,我們可以逐步求解,求出我們踩第一個雷的概率,接著是第二個雷的概率,以此類推。

很自然地,我們列出轉移方程式:

\[dp[i]=p*dp[i-1]+(1-p)*dp[i-2] \]

但是!這是一道多測題,單測都是極限的\(10^9\)​了,線性遞推肯定不可行,我們考慮優化。

仔細思考——嗯,這道題是不是有點似曾相識?看起來有點像斐波那契的遞推式不是嗎?那麼這道題也許可以用矩陣乘法來優化。

於是,我們能列出這麼一個矩陣的遞推式:

\[p 1-p\\ 1 0 \]

冪次方取得是每一段的長度。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Maxn 20000000
using namespace std;
typedef double db;
db p;
int n,pos[Maxn];
struct Matrix{
	double mat[2][2];
	void clear() {memset(mat,0,sizeof(mat));}
	void idenaty() {clear();for(int i=0;i<2;i++)mat[i][i]=1;}
};
Matrix mul(Matrix a,Matrix b) {
	Matrix res;
	res.clear();
	for(int i=0;i<2;i++) {
		for(int j=0;j<2;j++) {
			res.mat[i][j]=0;
			for(int k=0;k<2;k++) {
				res.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
			}
		}
	}
	return res;
}
Matrix power(Matrix x,int p) {
	Matrix res;
	res.idenaty();
	Matrix base=x;
	while(p) {
		if(p&1) res=mul(res,base); 
		base=mul(base,base);
		p>>=1;
	}
	return res;
}
int main() {
	while(scanf("%d%lf",&n,&p)!=EOF) {
		for(int i=1;i<=n;i++) 
			scanf("%d",&pos[i]);
		db ans=1;
		sort(pos+1,pos+n+1);
		Matrix a;
		a.clear();
		a.mat[0][0]=p;a.mat[0][1]=1-p;
		a.mat[1][0]=1;a.mat[1][1]=0;
		Matrix ret;
		ret.clear();
		ret=power(a,pos[1]-1);	
		ans*=(1-ret.mat[0][0]);
		for(int i=2;i<=n;i++) {
			if(pos[i]==pos[i-1]) {continue;}
			ret.clear();
			ret=power(a,pos[i]-pos[i-1]-1);
			ans*=(1-ret.mat[0][0]);
		}
		printf("%.7lf\n",ans);
	}
	return 0;
}