[CSP校内集训]v(记忆化搜索+map优化状压)
题意
有一个长为\(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;
}