【Codeforces Round 275 (Div 2)E】【状压DP 概率DP求期望 线性相加思想 二进制系统函数】Game with Strings n个串目标串随机猜位置随机的猜出答案的期望步

来源:互联网 发布:linux shell 复制粘贴 编辑:程序博客网 时间:2024/05/18 00:50
E. Game with Strings
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You play the game with your friend. The description of this game is listed below.

Your friend creates n distinct strings of the same length m and tells you all the strings. Then he randomly chooses one of them. He chooses strings equiprobably, i.e. the probability of choosing each of the n strings equals . You want to guess which string was chosen by your friend.

In order to guess what string your friend has chosen, you are allowed to ask him questions. Each question has the following form: «What character stands on position pos in the string you have chosen?» A string is considered guessed when the answers to the given questions uniquely identify the string. After the string is guessed, you stop asking questions.

You do not have a particular strategy, so as each question you equiprobably ask about a position that hasn't been yet mentioned. Your task is to determine the expected number of questions needed to guess the string chosen by your friend.

Input

The first line contains a single integer n (1 ≤ n ≤ 50) — the number of strings your friend came up with.

The next n lines contain the strings that your friend has created. It is guaranteed that all the strings are distinct and only consist of large and small English letters. Besides, the lengths of all strings are the same and are between 1 to 20 inclusive.

Output

Print the single number — the expected value. Your answer will be considered correct if its absolute or relative error doesn't exceed10 - 9.

Sample test(s)
input
2aabaac
output
2.000000000000000
input
3aaAaBaCaa
output
1.666666666666667
input
3acavacwqq
output
1.000000000000000
Note

In the first sample the strings only differ in the character in the third position. So only the following situations are possible:

  • you guess the string in one question. The event's probability is ;
  • you guess the string in two questions. The event's probability is  ·  =  (as in this case the first question should ask about the position that is other than the third one);
  • you guess the string in three questions. The event's probability is  ·  ·  = ;

Thus, the expected value is equal to 

In the second sample we need at most two questions as any pair of questions uniquely identifies the string. So the expected number of questions is .

In the third sample whatever position we ask about in the first question, we immediately identify the string.


【Codeforces Round 275 (Div 2)E】【状压DP 概率DP求期望 线性相加思想 二进制系统函数】Game with Strings n个串目标串随机猜位置随机的猜出答案的期望步数

#include<stdio.h>#include<iostream>#include<string.h>#include<string>#include<ctype.h>#include<math.h>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}#define MS(x,y) memset(x,y,sizeof(x))#define MC(x,y) memcpy(x,y,sizeof(x))#define MP(x,y) make_pair(x,y)#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}const int N=50+2,M=20+2,S=1<<20,Z=1e9+7,ms63=1061109567;int n,m;LL b[N];//b[i]表示:1<<ichar s[N][M];//s[][]用来存储字符串LL amb[S];//amb[sta]表示:位置选择的二进制表示sta时,有哪些串,是分辨不出来的double c[M][M];double p[M];//p[i]表示:到达一个猜了i个位置状态的概率void solve(){for(int i=0;i<=50;++i)b[i]=1ll<<i;for(int i=0;i<n;++i){for(int j=i+1;j<n;++j){int sta=0;for(int pos=0;pos<m;++pos)if(s[j][pos]==s[i][pos])sta|=b[pos];amb[sta]|=b[i];amb[sta]|=b[j];}}int top=1<<m;for(int sta=top;sta>=0;--sta){for(int i=0;i<m;++i)if(sta&b[i])amb[sta^b[i]]|=amb[sta];}c[0][0]=1;for(int i=1;i<=m;++i)    {        c[i][0]=1;        for(int j=1;j<=m;++j)        {            c[i][j]=c[i-1][j-1]+c[i-1][j];        }    }    for(int i=0;i<m;++i)p[i]=1.0/c[m][i];double ans=0;for(int sta=0;sta<top;++sta){ans+=__builtin_popcountll(amb[sta])*p[__builtin_popcountll(sta)];}printf("%.15f\n",ans/n);}int main(){while(~scanf("%d",&n)){for(int i=0;i<n;++i)scanf("%s",s[i]);m=strlen(s[0]);solve();}return 0;}/*【trick&&吐槽】这种DP还真是难呀。要不停尝试,横着来,横着不行竖着来,竖着不行再横着。不断来回倒腾,才能最终AC这道题~啦啦啦。【题意】有n(1<=n<=50)个串,串由小写字符或大写字符构成。串的长度在[1,20]之间。我们知道所有的串,而我的朋友等概率随机选取了其中一个串。我们想去猜,朋友选取的串是哪个串。于是我们每次会随机猜一个之前没有猜过的位置,然后朋友会告诉我,它选取的串的这个位置的字符是什么。如果在某个时刻,备选的串变成了唯一。那么我们就猜出了答案。现在问你,我们猜出目标串的步数的期望是多少。【类型】状压DP 期望DP【分析】这道题,首先有一个——"猜出的位置是影响问题的因素,位置之间不需要考虑先后顺序"的的性质。于是,我们可以考虑用二进制表示哪些位置选取了,这里的时间复杂度是2^m,最大只有2^20,约为1e6.然后,之后我们的复杂度,最大是n*2^m或者m*2^m。否则就会TLE。首先,我们想,我们大概可以枚举目标串的。如果知道了目标串,我们就可以知道,选取的位置为sta的时候,我们是否已经可以确定答案。具体做法是——用f[i][sta]表示,目标串是i号串,位置状态是sta的情况下,合法串的集合。之前先做一个预处理,用w[i][j]表示,如果第i个串是目标串,那么在位置j处与其有相同字符的串有哪些(用二进制LL表示)这个预处理可以用O(n * m * n)的时间复杂度暴力得到。也可以通过 dp[位置][~字符~]=对应满足要求的字符串集合LL的方式,用O(n*m)的时间复杂度得到。for(目标串){for(当前位置状态){找到任意一个可以拓展到它的状态,可以预处理"最后一个1的位置",也可以用系统函数中的__builtin_ctz()求出,记做pos然后就可以有这样一个状态转移:f[目标串][当前位置状态]=(f[目标串][上一个位置状态]&w[目标串][pos]);设()内的值为val,它表明,对于此目标串,位置状态是sta的情况下,合法串的集合然后,如果val==目标串的二进制表示,或者__builtin_popcountll(val)==1,代表我们可以确定目标串了。我们把所有(目标串,位置状态)确定的目标串,存放在这个位置状态中,即|进fix[sta]。以进行接下来的DP转移。}}然后我们可以开始DP了——用p[sta]表示,达到位置状态sta的概率。for(位置状态){得到现在确定的串for(下一个位置){得到下一个位置状态与下一个状态可以确定的串(现在确定的串的集合)^(接下来确定的串的集合)= 上一步并没有确定,这一步确定的串的集合我们现在可以得到:1,走到这一步决策的概率;2,步数;3,这一步所确定的串的数量。把位置概率*当步确定的串的数量*步数 统计到一个全局的ans中,最后的ans就是期望的步数。然后这道题就做完啦!}}=================================================我们再看看别人是如何做这道题的。1,对于两个字符串(i,j),我们求出字符串j的哪些位置与字符串i是相同的,存在一个二进制中。这个二进制再存下在这个二进制状态下可能会产生不确定关系的所有字符串的二进制表示。2,从大到小枚举位置状态,再枚举这个状态中有的位置。我们就可以传递不确定关系。可以知道某个位置状态下,可能是哪些字符串。3,从小到大枚举所有状态,得到这个状态已经选取了哪些位置。对于这个状态的所有后继状态,积累达到下一个状态的概率对于这个状态还不确定的位置,我们便要增加一次猜数,积累概率*步长即可。我们发现——其实,对于每个状态,只要积累到达这个状态的概率*这个状态不确定字符串的个数。最后/n就是答案。【时间复杂度&&优化】O((n+m)*2^m)*/

【Codeforces Round 275 (Div 2)E】【状压DP 概率DP求期望 二进制系统函数 旧写法】Game with Strings n个串目标串随机猜位置随机的猜出答案的期望步数

#include<stdio.h>#include<iostream>#include<string.h>#include<string>#include<ctype.h>#include<math.h>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}#define MS(x,y) memset(x,y,sizeof(x))#define MC(x,y) memcpy(x,y,sizeof(x))#define MP(x,y) make_pair(x,y)#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}const int N=50+2,M=50+2,S=1<<20,Z=1e9+7,ms63=1061109567;int n,m;LL b[N];//b[i]表示:1<<ichar s[N][M];//s[][]用来存储字符串LL w[N][M];//w[i][j]表示:在位置j处的字符,与i号串相同的字符串的集合int num1[S];//num1[sta]表示:二进制状态表示为sta的数中有多少个1,相当于__builtin_popcountll(sta)int lst1[S];//lst1[sta]表示:二进制状态表示为sta的数中,最后一个1的位置,相当于__builtin_ctz(sta);LL fix[S];//fix[sta]表示:位置选择的二进制表示sta时,有哪些串,已经能够分辨出来了LL f[S];//f([i])[sta]表示:当目标串为i,位置选择的二进制表示为sta时,有哪些串,依然与目标串一致。double p[S];//p[sta]表示:达到位置选择的二进制表示为sta下的概率void solve(){for(int i=0;i<=50;++i)b[i]=1ll<<i;for(int pos=0;pos<m;++pos){for(int i=0;i<n;++i){w[i][pos]=0;for(int j=0;j<n;++j)if(s[j][pos]==s[i][pos])w[i][pos]|=b[j];}}int top=1<<m;for(int sta=0;sta<top;++sta){num1[sta]=0;lst1[sta]=-1;for(int x=sta,p=0;x;x>>=1,++p){num1[sta]+=x&1;if((x&1)&&lst1[sta]==-1)lst1[sta]=p;}}MS(fix,0);for(int i=0;i<n;++i){f[0]=b[n]-1;for(int sta=1;sta<top;++sta){int pos=lst1[sta];f[sta]=f[sta^b[pos]]&w[i][pos];if(f[sta]==b[i])fix[sta]|=b[i];}}MS(p,0);p[0]=1;double ans=0;for(int sta=0;sta<top-1;++sta){double prb=p[sta]/(m-num1[sta]);//!小心除0!for(int i=0;i<m;++i)if(!(sta&b[i])){int nxt=sta|b[i];p[nxt]+=prb;ans+=prb*__builtin_popcountll(fix[nxt]^fix[sta])*num1[nxt];//目标期望=∑概率 * 确定数的个数 * 确定数的步长}}if(n==1)ans=0;//!小心把边界错误特判掉!printf("%.15f\n",ans/n);}int main(){while(~scanf("%d",&n)){for(int i=0;i<n;++i)scanf("%s",s[i]);m=strlen(s[0]);solve();}return 0;}/*【trick&&吐槽】这种DP还真是难呀。要不停尝试,横着来,横着不行竖着来,竖着不行再横着。不断来回倒腾,才能最终AC这道题~啦啦啦。不要忘记避免除法0不要忘记边界特判【题意】有n(1<=n<=50)个串,串由小写字符或大写字符构成。串的长度在[1,20]之间。我们知道所有的串,而我的朋友等概率随机选取了其中一个串。我们想去猜,朋友选取的串是哪个串。于是我们每次会随机猜一个之前没有猜过的位置,然后朋友会告诉我,它选取的串的这个位置的字符是什么。如果在某个时刻,备选的串变成了唯一。那么我们就猜出了答案。现在问你,我们猜出目标串的步数的期望是多少。【类型】状压DP 期望DP【分析】这道题,首先有一个——"猜出的位置是影响问题的因素,位置之间不需要考虑先后顺序"的的性质。于是,我们可以考虑用二进制表示哪些位置选取了,这里的时间复杂度是2^m,最大只有2^20,约为1e6.然后,之后我们的复杂度,最大是n*2^m或者m*2^m。否则就会TLE。首先,我们想,我们大概可以枚举目标串的。如果知道了目标串,我们就可以知道,选取的位置为sta的时候,我们是否已经可以确定答案。具体做法是——用f[i][sta]表示,目标串是i号串,位置状态是sta的情况下,合法串的集合。之前先做一个预处理,用w[i][j]表示,如果第i个串是目标串,那么在位置j处与其有相同字符的串有哪些(用二进制LL表示)这个预处理可以用O(n * m * n)的时间复杂度暴力得到。也可以通过 dp[位置][~字符~]=对应满足要求的字符串集合LL的方式,用O(n*m)的时间复杂度得到。for(目标串){for(当前位置状态){找到任意一个可以拓展到它的状态,可以预处理"最后一个1的位置",也可以用系统函数中的__builtin_ctz()求出,记做pos然后就可以有这样一个状态转移:f[目标串][当前位置状态]=(f[目标串][上一个位置状态]&w[目标串][pos]);设()内的值为val,它表明,对于此目标串,位置状态是sta的情况下,合法串的集合然后,如果val==目标串的二进制表示,或者__builtin_popcountll(val)==1,代表我们可以确定目标串了。我们把所有(目标串,位置状态)确定的目标串,存放在这个位置状态中,即|进fix[sta]。以进行接下来的DP转移。}}然后我们可以开始DP了——用p[sta]表示,达到位置状态sta的概率。for(位置状态){得到现在确定的串for(下一个位置){得到下一个位置状态与下一个状态可以确定的串(现在确定的串的集合)^(接下来确定的串的集合)= 上一步并没有确定,这一步确定的串的集合我们现在可以得到:1,走到这一步决策的概率;2,步数;3,这一步所确定的串的数量。把位置概率*当步确定的串的数量*步数 统计到一个全局的ans中,最后的ans就是期望的步数。然后这道题就做完啦!}}=================================================我们再看看别人是如何做这道题的。1,对于两个字符串(i,j),我们求出字符串j的哪些位置与字符串i是相同的,存在一个二进制中。这个二进制再存下在这个二进制状态下可能会产生不确定关系的所有字符串的二进制表示。2,从大到小枚举位置状态,再枚举这个状态中有的位置。我们就可以传递不确定关系。可以知道某个位置状态下,可能是哪些字符串。3,从小到大枚举所有状态,得到这个状态已经选取了哪些位置。对于这个状态的所有后继状态,积累达到下一个状态的概率对于这个状态还不确定的位置,我们便要增加一次猜数,积累概率*步长即可。我们发现——其实,对于每个状态,只要积累到达这个状态的概率*这个状态不确定字符串的个数。最后/n就是答案。【时间复杂度&&优化】O((n+m)*2^m)*/


0 0
原创粉丝点击