题意
有一个长为\(n\)的01序列,每次等概率从\([1,len]\)中抽取一个\(x\),可以选择从右向左或是从左往右的第\(x\)个数字取走,求\(k\)次操作后取走1的期望个数\((k\leq n\leq 30)\)
思路
然而就是直接状态压缩
以24为分界线,24步以内用状态压缩;24步以上可以发现状态很大但状态数很少(因为只进行了几次操作),用\(map\)存
对于每种状态枚举\(x\)记忆化搜索即可,需要大力卡常
二进制下去掉第\(p\)位:\(x=((x>>p)<<(p-1)) | (x\&((1<<p)-1))\)
Code
(因为我的代码太菜了卡不过常数,所以放的别人的)
#include<bits/stdc++.h> #define maxn 35 #define LL long long using namespace std; int n,m,a[maxn]; char s[maxn]; double ans,f[(1<<24)+5]; const double eps=1e-10; LL bin,num; map< LL,double > mp[31]; inline void work(int x,int len) { x=len-x+1; int y=bin&((1<<(x-1))-1); bin=((bin>>x)<<(x-1))|y; } double dfs(int now) { if(now==m+1 || bin==0) return 0.0; double res=0.0; LL tmp=bin; int len=n-now+1; if(len<24 && f[1<<len|bin]!=-1) return f[1<<len|bin]; if(mp[now].count(bin)) return mp[now][bin]; for(int i=1;i<=(len+1)/2;i++) { double qwq=0.0; int x=(bin>>(len-i))&1; work(i,len); if((len&1) && i==(len+1)/2) qwq=(x+dfs(now+1))*1.0/len; else qwq=(x+dfs(now+1))*2.0/len; bin=tmp; x=(bin>>(len-(n-now+2-i)))&1; work(n-now+2-i,len); if((len&1) && i==(len+1)/2) qwq=max(qwq,(x+dfs(now+1))*1.0/len); else qwq=max(qwq,(x+dfs(now+1))*2.0/len); bin=tmp; res+=qwq; } if(len<24) return f[1<<len|bin]=res; else return mp[now][bin]=res; } int main() { freopen("v.in","r",stdin); freopen("v.out","w",stdout); scanf("%d%d%s",&n,&m,s+1); for(int i=1;i<=n;i++) a[i]=(s[i]=='W')?1:0,bin=(bin<<1LL)|a[i]; for(int i=1;i<(1<<24);i++) f[i]=-1; printf("%.10lf\n",dfs(1)); return 0; }