玩个球

玩个球

《关于我继“贞难调”后又命名了一道“贞卡常”的这件事》

其实这道题部分分还是比较好拿的。

对于subtask3,k=0明显就是一个都不拿,而k=n就是全都拿。

对于subtask4,如果全为W就输出k,否则就输出0

然后我们就愉快的拿到2pts了。

对于subtask5,就直接统计这个W被拿到的概率,通过容斥可以求出,其概率为1- \prod_{i=n-k+1}^{n} \frac{1}{i}

对于subtask6,也可以通过上面方法得到,不过有些麻烦。不过因为数据过水,直接输出k也可以得分

对于subtask1,2,我们可以通过状压dp实现,状态i表示那些点已经被取出,dp_{i}表示在状态为i的情况下可以取出的最多白球数,可得到状态转移方程式,dp_{i}=\sum_{j=1}^{\left| i \right |}\frac{max\{dp_{i\oplus j},dp_{i\oplus \left| i\right|-j+1}\}}{\left| i \right |}

其转移过程可以通过记忆化dp进行实现,这样的时间复杂度是O\left(n2^n \right ),可以过掉前两个subtask。

对于subtask7,很容易发现,上面的做法,不算时间,光是空间都会炸,所以我们需要用map去维护dp值。但这样还是会T,考虑优化。我们发现,许多状态其实是重复的,因为根据他们得到剩余的序列是一样的。于是,我们考虑根据根据最后剩余的序列作为状态,来进行状态压缩。令dp_{i,j}表示剩余一个长度为i的序列,剩余球状态为j时可以得到的期望最大球数,状态转移方程式与上面的差不多。

可笔者还是会T on test subtask 32。因为map常数太大了,需要将其换成unordered_map,即使这样还是会T。

于是,我们还需要将状压dp数组拆成两端,一段状态数大的用map存,一段状态数小的用数组存,才卡得过去。

还是笔者常数太大了

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<tr1/unordered_map>
using namespace std;
using namespace tr1;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
} 
int n,k;
unordered_map<int,double>F[10];
unordered_map<int,bool>Vis[10];
bool vis[22][(1<<21)+5];
double f[22][(1<<21)+5];
char maze[35];
double Max(double a,double b){return a>b?a:b;}
double dfs(int len,int S,int sum){
	if(len>21){
		if(Vis[len-21][S])return F[len-21][S];
		//printf("%d %d %d\n",len,S,sum);
		Vis[len-21][S]=1;if(len==n-k)return F[len-21][S]=1.0*sum;
		double tmp=1.0/(1.0*len);
		for(int i=1;i<=(len+1)/2;i++){
			int t1=i,t2=len-i+1;
			int S1=((S>>t1)<<t1-1)|(S&((1<<t1-1)-1)),s1=(S>>t1-1)&1;
			int S2=((S>>t2)<<t2-1)|(S&((1<<t2-1)-1)),s2=(S>>t2-1)&1;
			if(t1==t2)F[len-21][S]+=tmp*dfs(len-1,S1,sum+s1);
			else F[len-21][S]+=2.0*tmp*Max(dfs(len-1,S1,sum+s1),dfs(len-1,S2,sum+s2));
		}
		//printf("%d %d:%lf\n",len,S,f[len][S]);
		return F[len-21][S];
	}
	else{
		if(vis[len][S])return f[len][S];
		//printf("%d %d %d\n",len,S,sum);
		vis[len][S]=1;if(len==n-k)return f[len][S]=1.0*sum;
		double tmp=1.0/(1.0*len);
		for(int i=1;i<=(len+1)/2;i++){
			int t1=i,t2=len-i+1;
			int S1=((S>>t1)<<t1-1)|(S&((1<<t1-1)-1)),s1=(S>>t1-1)&1;
			int S2=((S>>t2)<<t2-1)|(S&((1<<t2-1)-1)),s2=(S>>t2-1)&1;
			if(t1==t2)f[len][S]+=tmp*dfs(len-1,S1,sum+s1);
			else f[len][S]+=2.0*tmp*Max(dfs(len-1,S1,sum+s1),dfs(len-1,S2,sum+s2));
		}
		//printf("%d %d:%lf\n",len,S,f[len][S]);
		return f[len][S];
	}
}
int main(){
	//freopen("v.in","r",stdin);
	//freopen("v.out","w",stdout);
	read(n);read(k);scanf("\n%s",maze);
	if(k==0){printf("%.7lf\n",0);return 0;}
	if(k==n){
		int sum=0;
		for(int i=0;i<n;i++)sum+=(maze[i]=='W');
		printf("%.7lf\n",1.0*sum);
		return 0;
	}
	int sumW=0;
	for(int i=0;i<n;i++)sumW+=(maze[i]=='W');
	if(sumW==0){printf("%.7lf\n",0);return 0;}
	if(sumW==n){printf("%.7lf\n",1.0*k);return 0;}
	if(sumW==1){
		double gl=1;
		for(int i=0;i<k;i++){
			int tmp=n-i;
			gl*=1.0*(tmp-2)/tmp;
		}
		printf("%.7lf\n",1.0-gl);
		return 0;
	}
	if(sumW==n-1){
		printf("%.7lf\n",1.0*k);
		return 0;
	}
	int num=0;
	for(int i=0;i<n;i++)num<<=1,num|=(maze[i]=='W');
	printf("%.7lf\n",dfs(n,num,0));
	return 0;
}

谢谢!!!

posted @ 2020-08-26 20:06  StaroForgin  阅读(17)  评论(0)    收藏  举报  来源