牛客练习赛49 B. 筱玛爱阅读

链接

https://ac.nowcoder.com/acm/contest/946/B

题意

有 $n$ $(n\le 15)$ 本书的书店,店里在搞促销活动,包含若干个促销方案。每个促销方案是由指定的若干本书构成的集合,如果购买了该方案中所有的书,那么其中最便宜的一本书将免费。但是,每本书只能用于一个促销方案。

筱玛会得到 $n$ 个价格标签。筱玛可以给每本书挑选一个价格标签,使得每个价格标签和每本书一一对应。

筱玛想要知道,在合理利用所有促销方案的情况下,买下所有书最小要多少钱。

思路

将 $n$ 个价格降序排序,考虑状压 DP。

记 $dp_i$ 为状态 $i$ (第 $x$ 位为 $1$ 表示买下了第 $x$ 本书)的最大省下价格,$cnt_i$ 为状态 $i$ 购买书的数量,$p_i$ 为第 $i$ 大的价格。

对于状态 $i$,有如下转移:

  • 枚举状态 $i$ 最后一个使用的促销方案,即枚举 $i$ 的子集 $j$ ($j$ 可以等于 $i$),如果子集对于当前状态的补集是一种促销方案的话,可以转移,$dp_i=\max(dp_i,dp_{i \oplus j}+p_{cnt_{i}})$;
  • 枚举一本书不存在于任何一种促销方案中,因为可能有的书不存在促销方案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const int N=16;
int p[N],cnt[1<<N],dp[1<<N];
bool st[1<<N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;cin>>n>>m;
int sum=0;
for(int i=1;i<=n;i++) cin>>p[i],sum+=p[i];
sort(p+1,p+1+n,greater<int>());
for(int i=1;i<=m;i++) {
int x;cin>>x;
int bit=0;
for(int j=1;j<=x;j++) {
int t;cin>>t;
bit|=1<<t;
}
st[bit]=true;
}
for(int i=1;i<1<<n;i++) cnt[i]=cnt[i&(i-1)]+1;
for(int i=0;i<1<<n;i++) {
if(st[i]) dp[i]=max(dp[i],p[cnt[i]]);
for(int j=i;j;j=(j-1)&i) if(st[i^j]) {
dp[i]=max(dp[i],dp[j]+p[cnt[i]]);
}
for(int j=0;j<n;j++) if(i&(1<<j)) {
dp[i]=max(dp[i],dp[i^(1<<j)]);
}
}
cout<<sum-dp[(1<<n)-1]<<'\n';
return 0;
}