您的位置:首页 > 产品设计 > UI/UE

UVA1252 Twenty Questions dp & set

2016-07-29 10:59 351 查看
题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3693

Consider a closed world and a set of features that are defined for all the objects in the world. Each feature can be answered with
yes" or
no”. Using those features, we can identify any object from the rest of the objects in the world. In other words, each object can be represented as a fixed-length sequence of booleans. Any object is different from other objects by at least one feature.

You would like to identify an object from others. For this purpose, you can ask a series of questions to someone who knows what the object is. Every question you can ask is about one of the features. He/she immediately answers each question with
yes" or
no” correctly. You can choose the next question after you get the answer to the previous question.

You kindly pay the answerer 100 yen as a tip for each question. Because you don’t have surplus money, it is necessary to minimize the number of questions in the worst case. You don’t know what is the correct answer, but fortunately know all the objects in the world. Therefore, you can plan an optimal strategy before you start questioning.

The problem you have to solve is: given a set of boolean-encoded objects, minimize the maximum number of questions by which every object in the set is identifiable.

Input

The input is a sequence of multiple datasets. Each dataset begins with a line which consists of two integers, m and n: the number of features, and the number of objects, respectively. You can assume 0 < m≤11 and 0 < n≤128. It is followed by n lines, each of which corresponds to an object. Each line includes a binary string of length m which represent the value (
yes" or
no”) of features. There are no two identical objects.

The end of the input is indicated by a line containing two zeros. There are at most 100 datasets.

Output

For each dataset, minimize the maximum number of questions by which every object is identifiable and output the result.

Sample Input

8 1

11010101

11 4

00111001100

01001101011

01010000011

01100110001

11 16

01000101111

01011000000

01011111001

01101101001

01110010111

01110100111

10000001010

10010001000

10010110100

10100010100

10101010110

10110100010

11001010011

11011001001

11111000111

11111011101

11 12

10000000000

01000000000

00100000000

00010000000

00001000000

00000100000

00000010000

00000001000

00000000100

00000000010

00000000001

00000000000

9 32

001000000

000100000

000010000

000001000

000000100

000000010

000000001

000000000

011000000

010100000

010010000

010001000

010000100

010000010

010000001

010000000

101000000

100100000

100010000

100001000

100000100

100000010

100000001

100000000

111000000

110100000

110010000

110001000

110000100

110000010

110000001

110000000

0 0

Sample Output

0

2

4

11

9

题目大意:

有n个物体,m个特征。对每个物体给定一个01串,表示是否拥有该特征。对于任意物体,最少需要确定多少种特征使得它可以唯一确定?

例:1100与0110,只需确定第一位或者第三位即可唯一确定这两种物品。

解题思路:

状态转移方程:d[s][a] = max(d[s+k][a+k], d[s+k][a])+1;

利用cnt[s][a]保存同时满足s,a特征的个数,避免状态转移时的重复计算。

详见代码

// ConsoleApplication2.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"//VS2015下不可省略
#include<iostream>
#include<cstdio>
#include<ctime>
#include<algorithm>
#include<cstring>
#include<cassert>
using namespace std;
#define REN(i, n) for(int i = 0; i < n; i++)
//s集合表示已经询问过的特征,a集合保存满足已经询问过的特征
const int maxn = 128;//最大物品数目
const int maxm = 11;//最多特征种类

int kase, n, m;//kase表示当前的测试组编号
char obj[maxn][maxm + 100];//obj[i]存放第i种的特征01串
int vis[1 << maxm][1 << maxm], d[1 << maxm][1 << maxm];//vis[s][a]表示[s][a]                            //的特征组合是否访问过,记忆化搜索,d[s][a]存放[s][a]的最优解
int cnt[1 << maxm][1 << maxm];//cnt[s][a]存放符合[s][a]集合的物品数目

int dp(int s, int a)
{
if (cnt[s][a] <= 1) return 0;//如果满足条件的个数就一个,那么表示可以唯一确定
if (cnt[s][a] == 2) return 1;//如果为2,则只需在排除其中一个特征就可以唯一确定

int& ans = d[s][a];
if (vis[s][a] == kase) return ans;//记忆化搜索,已经查询过,那直接返回值
vis[s][a] = kase;//未查询过,标记成已查询
ans = m;//ans设置成最坏情况,即需要询问所有m个特征
REN(k, m)if (!(s&(1 << k)))//这是二进制的集合操作
{
int s2 = s | (1 << k), a2 = a | (1 << k);//更新s与a
if (cnt[s2][a2] >= 1 && cnt[s2][a] >= 1)//只有当这两者都大于或等于1时,才有继续计算的必要
{
int need = max(dp(s2, a2), dp(s2, a)) + 1;//max第一个参数表示具备该特征,第二个参数表示不具备该特征
ans = min(need, ans);
}
}
return ans;
}

void init()
{
REN(s, 1 << m)
{
for (int a = s; a; a = (a - 1)&s)//枚举出所有s的子集并初始化为0
cnt[s][a] = 0;
cnt[s][0] = 0;//由于a为零时退出了循环,所以需要另外赋值
}
REN(i, n)
{
int ftu = 0;
REN(f, m)if (obj[i][f] == '1')ftu |= (1 << f);//将01串转换成2进制
REN(s, 1 << m)cnt[s][s&ftu]++;//统计ftu的所有子集
}
}

int main()
{
memset(vis, 0, sizeof(vis));
while (cin >> m >> n&&n)
{
++kase;//使用kase来对vis赋值,可以减少每次初始化的麻烦
REN(i, n)cin >> obj[i];
init();
cout << dp(0, 0) << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  uva dp 二进制集合