codeforces 482C Game with Strings

真是一道神题=。=做了一上午

题目大意:给你N个等长字符串(N<=50),长度<=20,对方在其中等概率选择一个字符串,你每次等概率地询问一个位置的字符(不重复),问能够知道对方选择的是哪个字符串的期望询问次数。

因为长度小于等于20,所以可以比较显然地想到状压DP。接下来YY一下转移方程、

首先我们需要知道,按照当前猜的位置,能否得到答案。那么用一个数组f[i]表示猜的状态是f[i]时,还没有猜出的字符串的状态、

我们可以先找到哪些操作不能区分两个字符串。那么我们枚举两个字符串的序号i,j,那么对于每一位s[j][place]==s[i][place],我们都无法区分,则将当前猜测的状态或上1<<place。最终的状态也要或上(1<<j)|(1<<i)。

那么显然,f[i]=所有(1<<j)==0的f[i+(1<<j)]的异或和。这是因为如果f[i+(1<<j)]猜不出某个字符串,那么f[i]一定也猜不出。

这样,我们就求出了f数组。

接下来进行dp。先想一想方程怎么写:

首先,我们发现:当每个位置都猜出来了,也就是f[i]=0的情况,dp[i]也为0。然后,对于每一个没有猜过的位置,我们都要猜一次。所以先算出还有多少个位置没猜,记为tot。还有就是,由于对方选择的字符串也是随机的,所以要考虑每猜一次都有可能得到答案的情况。令cnt(i)为状态i中1的个数,接下来就可以转移了。

dp[i]=1+∑(zt&(1<<i)==0)dp[zt|(1<<i)]/tot*cnt(qf[zt|(1<<i)])/cnt(qf[zt])。

这是因为有cnt(qf[(1<<i)])/cnt(qf[zt])的概率我们要继续猜下去,剩下的概率是选择的字符串已经被区分出来了,贡献是0。

顺便说一句,一开始我用枚举每一位求cnt,死活TLE。最后看别人代码的时候,发现有个函数可以直接求int变量二进制位中1的个数。。。

那么总的复杂度就是O(2^L*L)啦,终于能跑过了。

附代码

#include<bits/stdc++.h>
#define N 55
#define L 20
using namespace std;
double dp[1<<L];
int d[1<<L],n;
char s[N][L+1];
long long qf[1<<L];
int done,zt,l;
inline double cnt(long long u){
	return (double)__builtin_popcount(int(u%(1<<25)))+__builtin_popcount(int(u/(1<<25)));
}
int cx[300];
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++) scanf("%s",s[i]); 
	if(n==1){
		puts("0");
		return 0;
	}
	qf[0]=(1ll<<n)-1;
	l=strlen(s[0]);
	for(int i=0;i<n;i++){
		for(int j=i+1;j<n;j++){
			int sav=0;
			for(int k=0;k<l;k++)
				if(s[i][k]==s[j][k]) sav|=(1<<k);
			qf[sav]|=(1LL<<i)|(1LL<<j); 
		}
	}
	for(int i=(1<<l)-1;i>=0;i--)
		for(int j=0;j<l;j++)
			if((i>>j)&1)
				qf[i^(1<<j)]|=qf[i]; 
	for(int zt=(1<<l)-1;zt>=0;zt--){
		if(qf[zt]==0) continue;
		int tot=0;
		for(int i=0;i<l;i++)
			if(!(zt&(1<<i))) tot++;
		dp[zt]=1;
		for(int i=0;i<l;i++)
			if(!(zt&(1<<i))) dp[zt]+=dp[zt|(1<<i)]/tot*cnt(qf[zt|(1<<i)])/cnt(qf[zt]);	
	}
	printf("%.15lf\n",dp[0]);
	return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值