noip2019トレーニングテストイベント(XII)A.メモリ(メモリ)

免責事項:この記事はブロガーオリジナル記事です、続くBY-SAのCC 4.0を著作権契約、複製、元のソースのリンクと、この文を添付してください。
このリンク: https://blog.csdn.net/tylon2006/article/details/100048454

説明

あなたは友人と記憶ゲームをプレイしています。
まず、友人はあなたにランダムに文字列の中から選択確率n個の同一の長さの文字列を示しました。
私たちが入手可能な情報で選択した文字列の友人を決定することができれば、あなたは、位置に正しい文字を求めることができます各ラウンドは、その後、ゲームはあなたのスコアが使用ラウンド数である、以上です。
あなたは本当に愚かではなく、任意の戦略だので、あなたは、このような各文字の確率として、メソッドを使用しているので場所を尋ねていない尋ねました。
今、あなたは知ってほしい、この場合には、あなたは望ましい結果の数を推測することを期待しています。


入力

ライン1は、第1の整数n、文字列の数を含みます。
2〜N + 1行の等しい長さの文字列を含む、それが大文字と小文字のみを含んでいます。


出力

1小数出力線は、所望の結果を推測10小数点以下を保持するための所望の数を表します。


ヒント

長さLの設定ストリング
20のデータについて、N、l≤10
データ30、N、l≤15
データ60、N、l≤20
データ100、n≤50、l≤20


溶液

データ圧縮前処理+ N、 ザ・ n個 * 2 リットル O(N * 2 ^ k)と 問題は、私は期待していないということです

前処理の数、各列の不確実な状態、DPは、暴力的なことができます。


コード

#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
}

おすすめ

転載: blog.csdn.net/tylon2006/article/details/100048454