筱玛爱阅读 (状压+子集dp) 牛客

链接: 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}范围内 1n15,1m2n1,1n15,1m2n1int
思路: 第一次遇到这种题,或许之前遇过,但已经忘了
每种方案可以看成一个集合,一个集合由它的子集与补集构成,所以一个集合可以由子集转移得到

  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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值