题目链接
给出一个例子来理解题意:m=5,n=4时有
10100
11000
00001
00010
从左到右表示1-m个特征,现在询问第一个位置就可以把原来四组分为{(1),(2)}和{(3),(4)} 然后再区分(1)和(2)可以询问位置2或3 区分(3)和(4)可以询问位置4或5。
假设所要猜的物体为w,我们用一个集合s表示所询问的特征,用集合a表示在集合s里面w所具有的特征,即a是s的子集。
d(s,a)表示询问过特征集s,确认w所具有的特征集为a,还需要询问的最小次数,下一次询问的特征为k。次数:max{d(s+{k},a+{k}),d(s+{k},a)}+1
#include<cstdio>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<iostream>
using namespace std;
const int maxn = 1 << 12;
const int INF = 0x3f3f3f3f;
char str[20];
int m, n;
int d[maxn][maxn], p[maxn], cnt[maxn][maxn];
int dp(int s, int a)
{
if (d[s][a] != INF)
return d[s][a];
int num = 0;
for (int i = 0; i < n; i++)
if ((p[i] & s)==a)
num++;
if (num <= 1)//如果只有一个或没有物体符合则需要需要询问的次数为0
{
d[s][a] = 0;
return 0;
}
for (int k = 0; k < m; k++)
{
if (s&(1 << k))
continue;
d[s][a] = min(d[s][a], max(dp(s | (1 << k),a | (1 << k)), dp(s | (1 << k),a)) + 1);
}
return d[s][a];
}
int main() {
while (scanf("%d%d", &m, &n)&&m!=0)
{
memset(p, 0, sizeof(p));
memset(d, INF, sizeof(d));
for (int i = 0; i < n; i++)
{
scanf("%s", str);
for (int j = 0; str[j]; j++)
{
if (str[j] == '1')
p[i] |= (1 << j);//用位运算来表示物体的特征
}
}
printf("%d\n", dp(0, 0));//一开始没有询问所以s和a为空集
}
return 0;
}