nssl1468-V【状压,数学期望,dfs】

正题


题目大意

n n n个球排成一排颜色不同,每次选择一个随机的 [ 1.. n ] [1..n] [1..n]中的 x x x,然后删掉第 x x x个或第 n − x + 1 n-x+1 nx+1个数,求删 k k k次之后删掉的白球最多,求删掉数量的期望值


解题思路

考虑状态压缩 d p dp dp,定义第一个 1 1 1所在位数表示剩下的球数, 1 1 1表示该位置是白球, 0 0 0表示该位置是黑球。设 f s f_s fs表示状态为 s s s时的最小期望值。

d e l ( s , i ) del(s,i) del(s,i)表示 s s s去掉第 i i i个球后的状态,有状态转移方程 f s = ∑ i = 1 l m a x { f d e l ( s , i ) + o n e ( s , i ) , f d e l ( s , n − i + 1 ) + o n e ( s , n − i + 1 ) } l f_s=\frac{\sum_{i=1}^lmax\{f_{del(s,i)}+one(s,i),f_{del(s,n-i+1)}+one(s,n-i+1)\}}{l} fs=li=1lmax{fdel(s,i)+one(s,i),fdel(s,ni+1)+one(s,ni+1)}

这样的时间复杂度为 O ( 2 n n ) O(2^nn) O(2nn)

但是有许多状态是没有使用的,我们对于小的 s s s可以用一个桶存,对于大的 s s s我们可以用 m a p map map来存即可


c o d e code code

#include
#include
#include
#include
#define XJQ using
#define AK namespace
#define IOI std
XJQ AK IOI;
const int N=35,Lim=1<<24;
int n,k;
double a[Lim];
map<int,double> m; 
char str[N];
bool live(int x,int l){
	int z=x|(1<<l);
	if(z<Lim)return (a[z]!=-1);
	return m.count(z);
}
double get(int x,int l){
	int z=x|(1<<l);
	if(z<Lim)return a[z];
	return m[z]; 
}
void change(int x,int l,double val){
	int z=x|(1<<l);
	if(z<Lim) a[z]=val;
	else m[z]=val;
	return;
}
int del(int x,int w)
{return ((x>>w+1)<<w)+x%(1<<w);} 
double dfs(int s,int l){
	if(l<=n-k)return 0;
	if(live(s,l))return get(s,l);
	double ans=0;
	for(int i=0;i<l;i++) 
		ans+=(max(dfs(del(s,i),l-1)+((s>>i)&1),dfs(del(s,l-i-1),l-1)+((s>>(l-i-1))&1)))/l;
	change(s,l,ans);
	return ans;
}
int main()
{
	scanf("%d%d",&n,&k);
	scanf("%s",str);int s=0;
	for(int i=0;i<Lim;i++)a[i]=-1;
	for(int i=0;i<n;i++)
		s|=((str[i]=='W')<<i);
	printf("%.6lf",dfs(s,n));
}

你可能感兴趣的:(深搜,数学期望)