链接: https://ac.nowcoder.com/acm/contest/946/B
题面:
筱玛是一个热爱阅读的好筱玛,他最喜欢的事情就是去书店买书啦!
一天,他来到一家有nn本书的书店,筱玛十分快乐,决定把这家店里所有的书全部买下来。
正巧今天店里在搞促销活动,包含若干个促销方案。每个促销方案是由指定的若干本书构成的集合,如果购买了该方案中所有的书,那么其中最便宜的一本书将免费。但是,每本书只能用于一个促销方案。
作为店里的VIP,筱玛会得到nn个价格标签。筱玛可以给每本书挑选一个价格标签,使得每个价格标签和每本书一一对应。
筱玛想要知道,在合理利用所有促销方案的情况下,买下所有书最小要多少钱
1
≤
n
≤
15
,
1
≤
m
≤
2
n
−
1
,
1
≤
n
≤
15
,
1
≤
m
≤
2
n
−
1
,
所
有
标
签
价
值
之
和
在
int
范
围
内
1\le n\le15,1\le m\le2^n-1,1≤n≤15,1≤m≤2n −1,所有标签价值之和在\texttt{int}范围内
1≤n≤15,1≤m≤2n−1,1≤n≤15,1≤m≤2n−1,所有标签价值之和在int范围内
思路: 第一次遇到这种题,或许之前遇过,但已经忘了
每种方案可以看成一个集合,一个集合由它的子集与补集构成,所以一个集合可以由子集转移得到
for(int i=1;i<(1<<n);i++) //i代表一个集合
{
for(int j=i;j;j=(j-1)&i) //j代表i的一个子集 j=(j-1)&i
{
int x=i^j; //x代表j关于i的补集 x=i^j
if(!vis[x])
continue;
dp[i]=max(dp[j]+a[bit[i]],dp[i]);
}
for(int j=0;j<n;j++)
{
dp[i|(1<<j)]=max(dp[i|(1<<j)],dp[i]);
}
}
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int n,m;
int dp[N];
int a[N];
int bit[N];
bool vis[N];
bool cmp(int x,int y)
{
return x>y;
}
int get(int x)
{
int cnt=0;
for(int i=0;i<n;i++)
if((1<<i)&x)
cnt++;
return cnt;
}
int main()
{
int sum=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=m;i++)
{
int k;
scanf("%d",&k);
int x=0;
for(int i=1;i<=k;i++)
{
int id;
scanf("%d",&id);
x|=(1<<(id-1));
}
vis[x]=1;
dp[x]=a[k];
}
for(int i=1;i<(1<<n);i++)
bit[i]=get(i);
for(int i=1;i<(1<<n);i++)
{
for(int j=i;j;j=(j-1)&i)
{
int x=i^j;
if(!vis[x])
continue;
dp[i]=max(dp[j]+a[bit[i]],dp[i]);
}
for(int j=0;j<n;j++)
{
dp[i|(1<<j)]=max(dp[i|(1<<j)],dp[i]);
}
}
printf("%d\n",sum-dp[(1<<n)-1]);
return 0;
}