3822 期望DP(記憶化搜尋)
阿新 • • 發佈:2018-12-11
這道題的難點就在於狀態的設計,如果你只想了一會就來看這篇題解,我建議你多方面想想狀態的設計之後,若還是沒有思路再來看題解。
主要思路:
第一眼看過去是不是很多人都想如何存棋盤的狀態,但是我們並不需要每一行,每一列是怎麼放得,只需要知道有幾行放了,有幾列放了就可以了。同樣也有可能放一顆棋子之後沒有新增的行和新增的列,這時候,就需要推公式了。
設有n行m列的棋盤。
狀態x,y表示已經放了x行y列。(由於後繼狀態只有4種,列舉即可)
注意在轉移時判斷後繼狀態是否合法。(不會可以看程式碼)
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]); } }