[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;
}
posted @ 2019-10-31 07:38  擅长平地摔的艾拉酱  阅读(154)  评论(0编辑  收藏  举报
/*取消选中*/