[CSP校内集训]v

六月ゝ 毕业季﹏ 提交于 2019-12-03 01:20:58

题意

有一个长为\(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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!