链接
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;
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; }
|