[藍橋杯][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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM