zoukankan      html  css  js  c++  java
  • 牛客练习赛49 B 筱玛爱阅读 (状压DP,子集生成)

    链接:https://ac.nowcoder.com/acm/contest/946/B
    来源:牛客网

    时间限制:C/C++ 1秒,其他语言2秒
    空间限制:C/C++ 262875K,其他语言525750K
    64bit IO Format: %lld
    题目描述
    筱玛是一个热爱阅读的好筱玛,他最喜欢的事情就是去书店买书啦!
    一天,他来到一家有
    n
    n本书的书店,筱玛十分快乐,决定把这家店里所有的书全部买下来。
    正巧今天店里在搞促销活动,包含若干个促销方案。每个促销方案是由指定的若干本书构成的集合,如果购买了该方案中所有的书,那么其中最便宜的一本书将免费。但是,每本书只能用于一个促销方案。
    作为店里的VIP,筱玛会得到
    n
    n个价格标签。筱玛可以给每本书挑选一个价格标签,使得每个价格标签和每本书一一对应。
    筱玛想要知道,在合理利用所有促销方案的情况下,买下所有书最小要多少钱。

    输入描述:
    第一行两个数
    n
    ,
    m
    n,m,分别表示书的本数和促销方案的种数。
    第二行
    n
    n个整数,表示每个价格标签上的标注的价格。
    接下来
    m
    m行,每行第一个数
    k
    k表示该促销方案包含书的数量。接下来
    k
    k个数,表示书的编号。
    输出描述:
    输出一行一个数,表示答案。
    示例1
    输入
    复制
    4 2
    2 3 2 4
    2 2 3
    2 2 4
    输出
    复制
    8
    备注:
    对于100%的数据,
    1

    n

    15
    ,
    1

    m

    2
    n

    1
    1≤n≤15,1≤m≤2n−1,所有标签价值之和在
    int
    int范围内。

    题意:

    思路:

    首先进行二进制状压,i代表的整数的二进制信息中的第i位为1,代表取第i位。

    然后对读入的书价进行降序排序,
    然后把m个方案数进行状压成now ,vis[now] =1 代表有now这个状态的优惠方案。

    定义状压DP的状态,dp[i] 代表买了i状态的书,最多优惠多少元。
    同时维护数组cnt[i] 代表 第i个状态买了几本书,即二进制状态中有多少个1,我们可以用dp来获得。
    那么dp[i] 有三种更新方式,
    1,有i这个优惠方案,那么dp[i] 的最小就可以优惠 a[cnt[i]] 元,(a是已排序后的数组)
    2,多个状态的补集为i,例如 j^x=i,那么dp[i] 就可以由先使用j方案的优惠,取得了a[cnt[j] 优惠,然后再用 x方案,因为x方案是j关于i状态的补集,所以x,j方案都使用后,x方案可以获得的优惠是a[cnt[i]] 元。
    3、一个不是在优惠方案中的数j,和其他状态来更新的。(这样可以确保每一个状态被更新到。)

    所以书的价格和是sum,
    那么 答案就是sum-dp[(1<<n)-1]

    细节见代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    #include <vector>
    #include <iomanip>
    #define ALL(x) (x).begin(), (x).end()
    #define rt return
    #define dll(x) scanf("%I64d",&x)
    #define xll(x) printf("%I64d
    ",x)
    #define sz(a) int(a.size())
    #define all(a) a.begin(), a.end()
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define pii pair<int,int>
    #define pll pair<long long ,long long>
    #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    #define MS0(X) memset((X), 0, sizeof((X)))
    #define MSC0(X) memset((X), '', sizeof((X)))
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define eps 1e-6
    #define gg(x) getInt(&x)
    #define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
    using namespace std;
    typedef long long ll;
    ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
    ll powmod(ll a,ll b,ll MOD){ll ans=1;while(b){if(b%2)ans=ans*a%MOD;a=a*a%MOD;b/=2;}return ans;}
    inline void getInt(int* p);
    const int maxn=1000010;
    const int inf=0x3f3f3f3f;
    /*** TEMPLATE CODE * * STARTS HERE ***/
    ll a[50];
    int n;
    int m;
    int vis[maxn];
    int cnt[maxn];
    ll dp[maxn];
    int main()
    {
        // freopen("D:\common_text\code_stream\in.txt","r",stdin);
        //freopen("D:\common_textcode_stream\out.txt","w",stdout);
        gbtb;
        cin>>n;
        cin>>m;
        repd(i,1,n)
        {
            cin>>a[i];
        }
        sort(a+1,a+1+n,greater<ll>());
        repd(i,1,m)
        {
            int k;
            cin>>k;
            int v;
            int now=0;
            repd(j,1,k)
            {
                cin>>v;
                now|=(1<<(v-1));
            }
            vis[now]=1;
        }
        // dp[i]代表买了i状态的书,最多可以优惠多少元。
        int maxstate=(1<<n)-1;// 最大二进制状态
        for(int i=1;i<=maxstate;++i)
        {
            cnt[i]=cnt[i&(i-1)]+1;// dp 求cnt[i],cnt[i]代表i的二进制中有多少个1,从i-lowbit(i)转移
        }
        // repd(i,1,maxstate)
        // {
        //     chu(cnt[i]);
        // }
        for(int i=0;i<=maxstate;++i)// 枚举所有二进制状态
        {
            if(vis[i])
                dp[i]=max(dp[i],a[cnt[i]]);// 用i方案来免费第cnt[i]贵的书
            // chu(i);
            for(int j=i;j;j=i&(j-1))// 枚举i的所有子集j
            {
    
                int x=j^i;// x是 j关于i的补集
                if(!vis[x])
                    continue;
                dp[i]=max(dp[i],dp[j]+a[cnt[i]]);
                // 因为 x^j=i,那么我们就用 x+j混合的方案来使得i状态的cnt[i]个书都买,最大优惠了dp[j]+a[cnt[i]]
            }
            for(int j=0;j<n;j++)// 尝试牺牲一本书(即全价买它),来保证更新所有状态
            {
                dp[i|(1<<j)]=max(dp[i|(1<<j)],dp[i]);
            }
        }
        ll sum=0ll;
        for(int i=1;i<=n;i++)
        {
            sum+=a[i];
        }
        // chu(dp[(1<<n)-1]);
        cout<<sum-dp[(1<<n)-1]<<endl;
        
        
        
        return 0;
    }
    
    inline void getInt(int* p) {
        char ch;
        do {
            ch = getchar();
        } while (ch == ' ' || ch == '
    ');
        if (ch == '-') {
            *p = -(getchar() - '0');
            while ((ch = getchar()) >= '0' && ch <= '9') {
                *p = *p * 10 - ch + '0';
            }
        }
        else {
            *p = ch - '0';
            while ((ch = getchar()) >= '0' && ch <= '9') {
                *p = *p * 10 + ch - '0';
            }
        }
    }
    
    
    
    
    本博客为本人原创,如需转载,请必须声明博客的源地址。 本人博客地址为:www.cnblogs.com/qieqiemin/ 希望所写的文章对您有帮助。
  • 相关阅读:
    Orcale分析函数OVER(PARTITION BY... ORDER BY...)的讲解
    Linux下安装Redmine(项目管理软件)
    CentOS5.4安装redmine详细步骤
    CentOS安装redmine 2后的简单配置
    在linux上安装redmine
    Linux安装MediaWiki
    Linux下安装配置MediaWiKi全过程
    用Navicat_SSH 连接数据库服务器
    基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用
    基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
  • 原文地址:https://www.cnblogs.com/qieqiemin/p/11153217.html
Copyright © 2011-2022 走看看