题目分析
如果通过暴力递归,用每一袋取或不取两种状态遍历分析每一种情况,则时间复杂度为 ,显然超时。于是,可以采用状态压缩优化。
观察到,糖果的种类最多有20个,小于32,那么可以用二进制表示糖果的种类,进而表示每一袋糖果的状态。用 i 表示二进制下糖果的状态(如 1010 表示共四种糖果且获得第二种和第四种糖果的情况) dp[i] 表示达到状态 i 所需的最小数量,a[j] 表示第 j 袋糖果的状态,就可以得到状态转移方程:
那么只需要输出 dp[1(<<m)]即可。
代码实现
#include <iostream>
#include <cstring>
using namespace std;
int n, m, k, t, a[110] = {}, dp[1050000] = {};
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j < k; j++) {
cin >> t;
a[i] |= (1 << (t - 1));//将第 i 袋糖果的二进制下状态求出
}
}
for (int i = 0; i < (1 << m); i++)dp[i] = -1;//初始化dp数组
dp[0] = 0;
for (int i = 0; i < (1 << m); i++) {
for (int j = 0; j < n; j++) {
if (dp[i] != -1) {//如果状态 i 被到达过
if (dp[i | a[j]] == -1 || dp[i | a[j]] > dp[i] + 1)dp[i | a[j]] = dp[i] + 1;//状态转移
}
}
}
cout << dp[(1 << m) - 1];
}