Twenty Questions UVA - 1252

题目传送门

思路:这个题目很巧妙啊,一看就是一个状压DP,但是这个题我觉得难点在于如何求得状态转移以后的状态,一开始想了很久都没有想出来,然后看了lrj的书上写的,确实十分的巧妙,我们用dp[s][a]来表示当前已经询问了集合s的问题,具有集合a特征的还要询问最少多少次才能求得最优解,然后我们可以预处理一个数组来记录每一个状态下有多少个满足的物体。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#include <sstream>

#define MAXN 150
#define MAXE 11
#define MAX_SIZE 8
#define INF 0x3fffffff
#define EPS 1e-6
#define MOD 1000000007
#define LL long long
#define ULL unsigned long long
#define pi 3.14159

using namespace std;

int n, m;
int arr[MAXN];
int dp[(1 << MAXE) + 10][(1 << MAXE) + 10];
int num[(1 << MAXE) + 10][(1 << MAXE) + 10];

int change(const string &str) {
    int ans = 0;
    for (int i = 0; i < str.length(); ++i) {
        ans = ans * 2 + str[i] - '0';
    }
    return ans;
}

void init() {
    memset(num, 0, sizeof(num));
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < (1 << m); ++j) {
            num[j][j & arr[i]]++;
        }
    }
    memset(dp, -1, sizeof(dp));
}

int DFS(int s, int a) {
    int &ans = dp[s][a];
    if (ans > -1) {
        return ans;
    }
    if (num[s][a] == 1) {
        return ans = 0;
    } else if (num[s][a] == 2) {
        return ans = 1;
    }
    ans = m;
    for (int i = 0; i < m; ++i) {
        if (!(s & (1 << i)) && num[s | (1 << i)][a | (1 << i)] && num[s | (1 << i)][a]) {
            int temp = max(DFS(s | (1 << i), a), DFS(s | (1 << i), a | (1 << i))) + 1;
            ans = min(ans, temp);
        }
    }
    return ans;
}

int main() {
    std::ios::sync_with_stdio(false);
    while (cin >> m >> n) {
        if (n == 0 && m == 0) {
            break;
        }
        string str;
        for (int i = 1; i <= n; ++i) {
            cin >> str;
            arr[i] = change(str);
        }
        init();
        cout << DFS(0, 0) << endl;
    }
    return 0;
}

/*
 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
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值