noip2019 training test events (xii) A. memory (memory)

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/tylon2006/article/details/100048454

Description

You're playing a memory game with friends.
First, a friend showed you n identical length string, then randomly selected from a medium probability of a string.
Each round you can ask for the correct character on a position, if we can with the available information to determine the string friend selected, then the game is over, your score is the number of rounds used.
Because you're really stupid, not any strategy, so you use a method, such as each character probability asked not asked a location.
Now you want to know, in this case, you expect to guess the number of the desired result.


Input

Line 1 comprises a first integer n, the number of strings.
Of 2 ~ n + 1 lines contains a string of equal length, it contains only uppercase letters and lowercase letters.


Output

1 a decimal output line, represents the desired number to guess the desired results, retain the 10 decimal places.


HINT

Set string of length l
for the 20 data, n, l≤10
the data 30, n, l≤15
the data 60, n, l≤20
the data 100, n≤50, l≤20


Solution

A data compression preprocessing + n, O ( n 2 l ) O (n * 2 ^ k) ,the problem is that I would not expect.

The number of pretreatment the uncertain state of each string, the DP can be violent.


Code

#include<bits/stdc++.h>
using namespace std;
int n,m,M;
const int N=1<<20;
int num[N],cnt[N],s[50][20];
long long a[50][52],b[50][N];
long double f[N],ans;
char str[20];
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%s",str);
		m=strlen(str),M=1<<m;
		for(int j=0;j<m;j++){
			s[i][j]=('a'<=str[j]&&str[j]<='z')?str[j]-'a':str[j]-'A'+26; //字符串
			a[j][s[i][j]]|=(1ll<<i);  //a[j][k]存储第j位是k的字符串有哪些(状压保存)
		}
	}
	//num[ss]代表状态为ss时有多少个串 当答案为该串时不确定(总)
	num[0]=n;  //ss为0时全满
	for(int i=0;i<n;i++){
		//b[i][j]代表选定的字符串是i,状态为j时有多少个不确定的串(分)
		b[i][0]=(1LL<<n)-1LL; //j为0时全满
		for(int j=1;j<M;j++){
			int k=j^(j&-j);
			int pos=__builtin_ctz(j); //求出lowbit在j的二进制表达中的位置
			b[i][j]=b[i][k]&a[pos][s[i][pos]]; //用还没有lowbit的状态来更新有lowbit状态
			//b[i][j]=b[i][j^lowbit(j)]&a[最低位][串i的最低位这个位置的字符]
			//意思就是b[i][k]已经存储了其他位的不确定串有哪些,但要剔除掉最低位这个位置的字符与串i不同的串
			if(b[i][j]!=(b[i][j]&-b[i][j])) num[j]++;
			//因为b[i][j]必将包含自身i,所以若b[i][j]不止一位,即该串为答案且状态为j时不确定
		} 
	}
	for(int i=1;i<M;i++)
	cnt[i]=cnt[i>>1]+(i&1); //求出1~1<<m-1每个数的二进制表示中一的个数,也是操作次数
	f[0]=1; //f[x]统计转移到x的概率
	for(int i=1;i<M;i++)
	for(int j=0;j<m;j++)
	if((i>>j)&1){ //即根据二进制位一位一位枚举 若该位为1才进行操作
		long double tmp=f[i^(1<<j)]/(m-cnt[i]+1); 
		//cnt[i]是1的个数,那么m-cnt[i]+1就是0的个数,多加1是因为当前的cnt[i]比cnt[i^(1<<j)]多1,而我们求的是原串0的个数
		//显然该位为1的状态要由该位为0的状态转移到,即该位变成1,概率为f[i^(1<<j)]*(1/(m-cnt[i]+1))
		ans+=cnt[i]*(tmp*(num[i^(1<<j)]-num[i]));
		//cnt[i]是操作次数,括号中的是操作cnt[i]次的概率,即期望次数=操作次数*概率
		//tmp应该好理解,即转移过来的概率;
		//后面计算从不确定变成确定的串的个数,那么我们选其中任意一个串作为答案都可以转移到j状态
		//易知num[i^(1<<j)]>=num[i]
		f[i]+=tmp; //统计总概率
	}
	printf("%.10Lf",ans/n); //因为是等概率选择答案串,即概率为1/n
}

Guess you like

Origin blog.csdn.net/tylon2006/article/details/100048454