1. 程式人生 > >3822 期望DP(記憶化搜尋)

3822 期望DP(記憶化搜尋)

這道題的難點就在於狀態的設計,如果你只想了一會就來看這篇題解,我建議你多方面想想狀態的設計之後,若還是沒有思路再來看題解。

主要思路:

第一眼看過去是不是很多人都想如何存棋盤的狀態,但是我們並不需要每一行,每一列是怎麼放得,只需要知道有幾行放了,有幾列放了就可以了。同樣也有可能放一顆棋子之後沒有新增的行和新增的列,這時候,就需要推公式了。

設有n行m列的棋盤。

狀態x,y表示已經放了x行y列。(由於後繼狀態只有4種,列舉即可)

E(x,y,cnt)=

E((x+1,y+1,cnt+1)+1)*(\frac{(n-x)*(m-y)}{n*m-cnt})+

E((x,y+1,cnt+1)+1)*(\frac{x*(m-y)}{n*m-cnt})+

E((x+1,y,cnt+1)+1)*(\frac{(n-x)*y}{n*m-cnt})+

E((x,y,cnt+1)+1)*(\frac{x*y-cnt}{n*m-cnt})

注意在轉移時判斷後繼狀態是否合法。(不會可以看程式碼)

AC程式碼:

#include<cstdio>
#include<cstring>
#define M 55
double dp[M][M][M*M];
int n,m;
void dfs(int x,int y,int cnt) {
	if(dp[x][y][cnt])return;//記憶化搜尋 
	if(x==n&&y==m) {//終止態 
		return;
	}
	int Sur=n*m-cnt;//還有多少個棋子可以選 
	if(x<n) {//前提條件 
		int chs=(n-x)*y;//到達此狀態可以選的棋子 
		if(chs) {
			dfs(x+1,y,cnt+1);
			dp[x][y][cnt]+=(double)chs/Sur*(dp[x+1][y][cnt+1]+1.0);
		}
	}
	if(y<m) {
		int chs=(m-y)*x;
		if(chs) {
			dfs(x,y+1,cnt+1);
			dp[x][y][cnt]+=(double)chs/Sur*(dp[x][y+1][cnt+1]+1.0);
		}
	}
	if(x*y>cnt) {
		int chs=y*x-cnt;
		if(chs) {
			dfs(x,y,cnt+1);
			dp[x][y][cnt]+=(double)chs/Sur*(dp[x][y][cnt+1]+1.0);
		}
	}
	if(x<n&&y<m) {
		int chs=(n-x)*(m-y);
		if(chs) {
			dfs(x+1,y+1,cnt+1);
			dp[x][y][cnt]+=(double)chs/Sur*(dp[x+1][y+1][cnt+1]+1.0);
		}
	}
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		memset(dp,0,sizeof(dp));//注意要清空陣列 
		scanf("%d%d",&n,&m);
		dfs(0,0,0);
		printf("%.12lf\n",dp[0][0][0]);
	}
}