[蓝桥杯][2019年第十届真题]糖果*(状压dp)

题目描述

糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种 口味编号 1 ∼ M。

小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而 是 K 颗一包整包出售。

幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知 道每包内的糖果口味。

给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖 果。

输入

第一行包含三个整数 N、M 和 K。
接下来 N 行每行 K 这整数 T1, T2, · · · , TK,代表一包糖果的口

输出

一个整数表示答案。如果小明无法品尝所有口味,输出 −1。

样例输入

6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2

样例输出

2

提示

对于30%的评测用例,1 ≤ N ≤ 20。 

对于所有评测样例,1≤N≤100,1≤M≤20,1≤K≤20,1


 思路

  • 用二进制的形式表示每包糖果的口味分布,以第一个样例举例:
    1.   1 1 2  --> 1 1 0 0 0 
    2.   1 2 3  --> 1 1 1 0 0
    3.        1 1 3  --> 1 0 1 0 0
    4.        2 3 5 --> 0 1 1 0 1
    5.        5 4 2 --> 0 1 0 1 1
    6.        1 5 2 --> 1 1 0 0 1
  • 用一个map记录糖果组合下来的口味分布:口味-包数
  • 遍历每一种口味,如果这个口味在该组合中没有,那就加上一包含有这个口味的糖果
  • 从相同的口味分布中取得包数最少的那一组,然后继续遍历。
  • flag记录是否存在组合

代码

#include<iostream>
#include<map>
using namespace std;

/*
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
*/
int main()
{
    int n, m, k;
    int pc[105];
    cin >> n >> m >> k;
    for (int i = 0; i < n; i++) {    // 状态压缩
        int pack = 0;
        for (int j = 0; j < k; j++) {
            int c;
            cin >> c;
            c--;
            pack |= (1 << c);
        }
        pc[i] = pack;
    }
    map<int, int>mc;
    mc[0] = 0;
    bool flag;
    for (int i = 0; i < m; i++) {  // 对每一种口味
        map<int, int>t;
        flag = false;
        for (auto p : mc) {    // 对每一种组合(口味-->包数)
            int a = p.first, b = p.second;
            if ((1 << i)&a) {    // 如果这个组合中有这个口味
                flag = true;    
                if (t.count(a)) // // 这个组合存在
                    t[a] = t[a] < b ? t[a]: b;
                else t[a] = b;
            }
            else {    // 如果没有这个口味
                for (int j = 0; j < n; j++) {    // 对每一包糖果
                    if (pc[j] & (1 << i)) {  // 如果该包糖果有这个口味,就加入组合
                        flag = true;
                        if (t.count(a | pc[j]))        // 这个口味分布存在
                            t[a | pc[j]] = t[a | pc[j]] < b + 1 ? t[a | pc[j]] : b + 1;
                        else t[a | pc[j]] = b + 1;
                    }
                }
            }
        }
        if (!flag) break;
        mc = t;
    }
    if (flag) {   // 若存在,则遍历所有组合,输出最小包数
        int res = 1 << 30;
        for (auto p : mc) {
            res = res<p.second?res:p.second;
        }
        cout << res;
    }
    else cout << "-1";

    return 0;
}

 

posted @ 2020-03-25 15:32  champanesupernova  阅读(1179)  评论(0编辑  收藏  举报