题目大意
有n个长度为m的二进制串,每个都是不同的。
为了把所有字符串区分开,你可以询问,每次可以问某位上是0还是1。
问最少提问次数,可以把所有字符串区分开来。
思路
好吧 这个题看了一下书上的思路
用s表示已经询问的集合,a是s的一个子集 表示在s的询问下每一位的状态.
d[s][a]表示询问s在a这种状态下还需要询问的最小次数
如果符合(s,a)的串的数目>1 那么还需要在询问
所以转移方程为 d[s][a]=d[s][a]=min(d[s][a] , max(dp(s+k,a+k),dp(s+k,a))+1);
边界为符合(s,a)的串的数目<=1 时d[s][a]=0
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
int n,m,w[150],d[(1<<11)+10][(1<<11)+10];
const int INF = 20;
int dp(int s,int a)
{
if(d[s][a]!=-1) return d[s][a];
int cnt=0;
for(int i=0;i<n;i++)
if((w[i]&s)==a) cnt++;
if(cnt<=1) return d[s][a]=0;
d[s][a]=INF;
for(int i=0;i<m;i++){
if(s&(1<<i)) continue;
d[s][a]=min(d[s][a],max(dp(s|(1<<i),a|(1<<i)),dp(s|(1<<i),a))+1);
}
return d[s][a];
}
int main()
{
while(scanf("%d%d",&m,&n))
{
if(!m&&!n) break;
memset(w,0,sizeof(w));
for(int i=0;i<n;i++){
char str[20];
scanf("%s",str);
for(int j=0;j<m;j++)
if(str[j]=='1') w[i]=w[i]|(1<<j);
}
memset(d,-1,sizeof(d));
printf("%d\n",dp(0,0));
}
return 0;
}