B 筱玛爱阅读
题目地址
题意:给定n个物品,m个方案,第i个方案包含 ki个物品,现在共有n个价格,但不确定每一个物品属于哪一个价格。问买下所有物品需要的最小价格。
分析:
- 简化问题,加入价格固定,就是一个状压dp,如果暴力递推,那么复杂度 O(2n∗m)=O(4n),不可接受,考虑采用枚举子集的方式复杂度 O(3n)
- 现在考虑价格不固定,先把物品从大到小排序,考虑新加入的最后一个子集优惠的是最后一个元素即可
参考代码:
#include<bits/stdc++.h>
using namespace std;
// 1 排序,从大到小
// 2 状压方案
// 3 枚举子集
const int maxn = 1 << 16;
int a[maxn];
int num[maxn], plan[maxn];
bool vis[maxn];
int dp[maxn];
int main(void) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i];
sort(a + 1, a + n + 1);
reverse(a + 1, a + n + 1);
for (int i = 1; i <= m; ++i) {
int k;
scanf("%d", &k);
for (int j = 1; j <= k; ++j) {
int a; scanf("%d", &a);
plan[i] |= 1 << (a - 1);
}
vis[plan[i]] = k;
}
for (int i = 0; i < (1 << n) - 1; ++i)
num[i] = __builtin_popcount(i);
int M = 0;
for (int i = 1; i < (1 << n) - 1; ++i) {
if (vis[i])
dp[i] = max(dp[i], a[num[i]]);
for (int s = i; s != 0; s = (s - 1)&i) {
int x = i ^ s;
if (!vis[x]) continue;
dp[i] = max(dp[i], dp[s] + a[num[i]]);
}
for (int j = 0; j < n; ++j)
dp[i | (1 << j)] = max(dp[i | (1 << j)], dp[i]);
M = max(M, dp[i]);
}
int sum = 0;
for (int i = 1; i <= n; ++i)
sum += a[i];
cout << sum - M << endl;
return 0;
}