nssl 1468.V

D e s c r i p t i o n Description Description

一个长度为 n n n的0/1序列,将会进行 k k k次删除操作,每次操作等概率的选取一个数字 x x x,你可以选择删除此时从左往右第 x x x位或者从右往左第 x x x位,删除完后,后面的数要补齐这个位置

问在最优策略下,期望能删除最多的1的个数

数据范围: n ≤ 30 n\leq 30 n30


S o l u t i o n Solution Solution

n n n比较小,我们考虑状压 d p dp dp,用一个 2 n 2^{n} 2n的二进制数来表示所有的状态,接着枚举被删除的位置,两着做最大值。由于是倒着做的,要用记忆化搜索,复杂度: O ( 2 n n ) O(2^nn) O(2nn),这样能拿到40分。

但其实,这个东西是肯定跑不满的,我们可以大胆去做,记忆化搜索在这题的瓶颈在于,有一些数过大没办法记录,开 m a p map map处理即可,在小于 l i m lim lim的时候用数组存,大于的时候用 m a p map map

这样理论上复杂度的上限是 ∑ f i b ( i ) \sum fib(i) fib(i)


C o d e Code Code

#include
#include
#include
#include
#include
using namespace std;int n,k,S;
double a[1<<24];
const double lim=1<<24;
char s[31];
map<int,double>m;
inline bool pd(int x,int l)
{
	int y=x|(1<<l);
	if(y<lim) return a[y]!=-1;
	return m.count(y);
}
inline double get(int x,int l)
{
	int y=x|(1<<l);
	if(y<lim) return a[y];
	return m[y];
}
inline void updata(int x,int l,double val)
{
	int y=x|(1<<l);
	if(y<lim) a[y]=val;
	else m[y]=val;
	return;
}
inline int dele(int x,int l){return ((x>>l+1)<<l)+x%(1<<l);}
inline double dfs(int s,int l)
{
	if(l<=n-k) return 0;
	if(pd(s,l)) return get(s,l);
	double ans=0;
	for(register int i=0;i<l;i++) ans+=max(dfs(dele(s,i),l-1)+((s>>i)&1),dfs(dele(s,l-i-1),l-1)+((s>>(l-i-1))&1))/l;
	updata(s,l,ans);
	return ans;
}
signed main()
{
	scanf("%d%d",&n,&k);
	scanf("%s",s);
	for(register int i=0;i<strlen(s);i++) S|=(s[i]=='W')<<i;
	for(register int i=0;i<lim;i++) a[i]=-1;
	printf("%.6lf",dfs(S,n));
}

你可能感兴趣的:(状压dp,记忆化搜索)