#数学期望,状压dp,记忆化搜索#nssl 1468 V


分析

赛时写了个(O(n!))的纯暴力,其实我现在才发现(O(n!))的暴力一般都能用(O(n2^n))的状压dp解决
但是其实不是每个状态都能被访问到,所以若(n)过大,用(map)存,否则用数组存,还有记忆化搜索优化时间,反正能过


代码

#include <cstdio>
#include <map>
#define rr register
using namespace std;
const int N=31,M=24; int n,k,now;
struct either{
	double ku[(1<<M)|1];
	map<int,double>uk[N];
    inline void Init(){for (rr int i=0;i<=(1<<M);++i) ku[i]=-1;}
    inline bool check(int now,int len){return len<M?(ku[(1<<len)|now]!=-1):uk[len].count(now);}
	inline double &Get(int now,int len){return len<M?ku[(1<<len)|now]:uk[len][now];}
}T;
inline signed shift(int now,int len){return ((now>>(len+1))<<len)|(now&((1<<len)-1));}
inline double dfs(int now,int len){
	if (len<=k) return 0;
	if (T.check(now,len)) return T.Get(now,len);
	rr double &ans=T.Get(now,len); ans=0;
	for (rr int i=0,j=len-1;i<=j;++i,--j){
		rr double t1=dfs(shift(now,i),len-1)+((now>>i)&1);
		if (i==j) {ans+=t1; continue;}
		rr double t2=dfs(shift(now,j),len-1)+((now>>j)&1);
		ans+=2*(t1>t2?t1:t2);
	}
	return ans/=len;
}
signed main(){
    scanf("%d%d",&n,&k),k=n-k,T.Init();
	for (rr int i=0;i<n;++i){
		rr char c=getchar();
		while (c!='W'&&c!='B') c=getchar();
		now|=(c=='W')<<i;
	}
	return !printf("%lf",dfs(now,n)); 
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13493081.html