• UVA1252 Twenty Questions


    vjudge传送门


    题面:有(n(n leqslant 128))个物体,(m(m leqslant 11))个特征。每个物体用一个(m)位01串表示,表示每个特征是否具备。我在心里想一个物体(W)(n)个之一),你来猜。每次可以询问一个特征,然后我会告诉你:(x)是否具备这个特征。当你确定之后,就把答案告诉我(告知答案不算“询问”)。如果采用最优策略,最少需要询问几次能保证猜到?例如,有两个物体:1100和0110,只要询问特征1或者特征3,就能1次猜到。


    突然感觉有点博弈论那味儿了。
    但这只是一道状压dp题。
    状态我觉得不太好想,可能是这题的思维不太一样吧。
    (dp[Q][A])表示当前问的问题集合是(Q)(Q)中是否具备的集合为(A)(Q,A)都是一个二进制压缩的状态,(A)(Q)的子集)时,最少还需几次能保证猜到。
    比如Q=10011010,A=00010010,那么能确定的特征序列就是0XX10X1X,所以我们只要看这个特征序列在(n)个物体中是否是独有的,如果是,就返回,否则继续dp。
    那么我们应该从Q=11111111的状态开始向前dp,用循环写起来比较费劲,所以为了简化代码,采用记忆化搜索的方式。


    现在只要知道怎么判断特征序列是否是独有的就好了:其实只要(Q & a[i]=A)就行了。
    我们分情况考虑:
    1.Q是0的位置:表示没有问到,那按位与后自然也是0,而A的这一位也是0,相等。
    2.Q是1的位置:表示问到了,因为我们要比较a[i]这一位是否与A相等,而按位与在这一位的结果只取决于a[i],那么自然就符合要求。


    时间复杂度是枚举子集的复杂度:(O(n3 ^ m))

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<queue>
    #include<assert.h>
    #include<ctime>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 130;
    const int maxm = 12;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    In void MYFILE()
    {
    #ifndef mrclr
    	freopen(".in", "r", stdin);
    	freopen(".out", "w", stdout);
    #endif
    }
    
    int n, m, a[maxn];
    char s[maxn];
    
    int dp[1 << maxm][1 << maxm];
    In int DP(int Q, int A)
    {
    	if(~dp[Q][A]) return dp[Q][A];
    	int cnt = 0;
    	for(int i = 1; i <= n; ++i) if((Q & a[i]) == A) ++cnt;
    	if(cnt <= 1) return dp[Q][A] = 0;		//符合该特征的物体最多有1个,答案确定,返回 
    	dp[Q][A] = INF;
    	for(int i = 0; i < m; ++i) if(!((Q >> i) & 1)) 
    		dp[Q][A] = min(dp[Q][A], max(DP(Q | (1 << i), A | (1 << i)), DP(Q | (1 << i), A)) + 1);
    	return dp[Q][A];
    }
    
    int main()
    {
    //	MYFILE();
    	while(scanf("%d%d", &m, &n) && m && n)
    	{
    		Mem(a, 0), Mem(dp, -1);
    		for(int i = 1; i <= n; ++i)
    		{
    			scanf("%s", s);
    			for(int j = 0; j < m; ++j) if(s[j] == '1') a[i] |= (1 << j);
    		}
    		write(DP(0, 0)), enter;
    	}
    	return 0;	
    }
    ``a
  • 相关阅读:
    自定义 spark transformer 和 estimator 的范例
    spark 与 scikit-learn 机器学习流程组件设计哲学比较
    命名空间和作用域
    FeatureUnion 与 ColumnTransformer 关系
    注解与装饰器
    装饰器编写--要点
    闭包结构的本质
    SQL 自动增长 identity
    SQL 基本的函数
    int和long long有符号整形 负数比正数多一个
  • 原文地址:https://www.cnblogs.com/mrclr/p/13839197.html
Copyright © 2020-2023  润新知