1. 程式人生 > 實用技巧 >BZOJ4832 [Lydsy2017年4月月賽]抵制克蘇恩 記憶化搜尋

BZOJ4832 [Lydsy2017年4月月賽]抵制克蘇恩 記憶化搜尋

概率DP+記憶化搜尋.

直接記憶化搜尋感覺比較顯然+簡單.

直接設狀態 $f[x][a][b][c]$ 表示還剩 $x$ 輪,當前牌的狀態為 $(a,b,c)$ 還期望造成的傷害.

轉移的話就是倒著轉移:$f[x][a][b][c] \leftarrow f[x-1][.....]$.

然後邊界的話將 $f[1][.....]$ 直接設為此時造成傷害的期望.

時間複雜度:$O(Tk8^3)$.

code:

#include <cstdio>  
#include <cstring>
#include <algorithm> 
#define N 54     
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
int vis[N][8][8][8]; 
double dp[N][8][8][8];   
double solve(int x,int a,int b,int c) { 
	if(vis[x][a][b][c]) {   	
		return dp[x][a][b][c];  
	}        	      
	double tmp=0.0; 
	tmp+=(solve(x-1,a,b,c)+1.0)/(a+b+c+1);  
	if(a) {           
		tmp+=solve(x-1,a-1,b,c)*1.0*a/(a+b+c+1);   
	}     
	if(b) {           
		if(a+b+c<7) tmp+=solve(x-1,a+1,b-1,c+1)*1.0*b/(a+b+c+1);      
		else { 	
			tmp+=solve(x-1,a+1,b-1,c)*1.0*b/(a+b+c+1);              
		}
	}  
	if(c) { 
		if(a+b+c<7) tmp+=solve(x-1,a,b+1,c)*1.0*c/(a+b+c+1);   
		else { 	
			tmp+=solve(x-1,a,b+1,c-1)*1.0*c/(a+b+c+1);  
		}
	}  
	vis[x][a][b][c]=1;   
	return dp[x][a][b][c]=tmp;  
}
int main() { 
	//setIO("input");     
	for(int i=0;i<8;++i) { 
		for(int j=0;j<8;++j) {      
			for(int k=0;k<8;++k) { 
				if(i+j+k>7) continue;    
				dp[1][i][j][k]=1.0/(i+j+k+1);    
				vis[1][i][j][k]=1;   
			}
		}
	}    
	int T,k,A,B,C; 
	scanf("%d",&T);  
	while(T--) { 
		scanf("%d%d%d%d",&k,&A,&B,&C);  
		printf("%.2f\n",solve(k,A,B,C));  
	}
	return 0;
}