典型的状压思想
设0表示黑球,1表示白球,用一串01序列代表剩下的球的状态,记f[i]表示在i状态下取球的最大期望
那么可以利用记忆化搜索更新,每一层枚举可能拿走的球然后向下搜索,同时记忆化即可
在状态中删去一个点可以利用位运算实现
同时要注意一个问题,就是状态0010和状态010并不是相同的状态,但是如果不做处理在记忆化的过程中很可能把他俩算成相同的状态,所以我们在初始状态最前面放一个1,这样就可以区分上述两种状态了
还有就是本题卡常卡的很厉害,所以对比较小的状态我们用数组,对过大的状态再使用map来操作即可
注意使用double
#include <cstdio>
#include <map>
#define ll unsigned int
using namespace std;
const ll con=(1<<25)+(1<<23)+(1<<22);
int n,k;
char s[35];
double ret=0;
int cct=0;
map <ll,double> M;
double f[con];
inline double max(double x,double y)
{
return x>y?x:y;
}
inline ll erase(ll sit,int pos)
{
ll temp=sit>>(pos-1);
temp<<=(pos-1);
ll ret=sit^temp;
sit>>=pos;
sit<<=(pos-1);
ret|=sit;
return ret;
}
inline int cot(ll sit)
{
int cyt=0;
for(int i=0;i<n-k;i++)
{
if((1<<i)&sit)
{
cyt++;
}
}
return cyt;
}
double dfs(int dep,ll sit)
{
if(sit<con&&f[sit])
{
return f[sit];
}else if(M[sit])
{
return M[sit];
}
if(dep==k+1)
{
double tot=(double)cct-cot(sit);
return tot;
}
double temp=0;
for(int i=1;i<=n-dep+1;++i)
{
int ri=n+2-i-dep;
double tt=0;
tt=max(tt,dfs(dep+1,erase(sit,i)));
tt=max(tt,dfs(dep+1,erase(sit,ri)));
temp+=tt;
}
if(sit<con)
{
return f[sit]=temp/(double)(n-dep+1);
}else
{
return M[sit]=temp/(double)(n-dep+1);
}
}
int main()
{
freopen("v.in","r",stdin);
freopen("v.out","w",stdout);
scanf("%d%d",&n,&k);
scanf("%s",s+1);
ll ori=0;
bool flag=0;
for(int i=1;i<=n;++i)
{
if(s[i]=='W')
{
ori|=1;
cct++;
}
if(s[i]!=s[i-1]&&i!=1)
{
flag=1;
}
ori<<=1;
}
if(k==0)
{
printf("0.000000000\n");;
return 0;
}
if(k==n)
{
printf("%.10lf\n",(double)cct);
return 0;
}
if(!flag)
{
if(s[1]=='W')
{
printf("%.10lf\n",(double)k);
}else
{
printf("0.000000000\n");
}
return 0;
}
ori>>=1;
ori|=(1<<n);
printf("%.10lf\n",(double)dfs(1,ori));
return 0;
}