zoukankan      html  css  js  c++  java
  • P3092 [USACO13NOV]没有找零No Change

    首先可以想到 **枚举** 用硬币的顺序.($O(K!)$) 假如硬币顺序确定了, 我们只需要确定每一个硬币买哪一段即可. 发现可以贪心, 即每一个硬币都尽量买, 直到剩余价值不够为止. 证明很简单: 假如我这个硬币可以多买某一个商品但是没有买, 只可能是为了用下一个硬币去买这个商品来让下一个硬币浪费的少一些. 但是下一个硬币少浪费的其实等于这个硬币多浪费的, 也就是这个商品的价格. 换言之, 这样不会更好, 但有可能更差(钱不够). 于是, 算出前缀和以后大力二分每一个硬币用到哪儿即可. 理论复杂度$O(K!KlogN)$, 显然无法承受.

    考虑状态压缩优化. 用一个数组 f[1 << 16] 记录下某一个状态时浪费价值的最小值.
    因为对于给定的状态, 金币的总价值确定, 可以很容易的结合记录下来的值推出当前买到哪一个商品, 再用前文所提到的二分更新状态.
    理论复杂度(O(2^N logN))

    #include <cstdio>
    #include <cstring>
    #include <cassert>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int INF = 0x7f7f7f7f;
    const int MAXN = 1e5 + 10;
    inline int read()
    {
        int x = 0; char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
        return x;
    }
    
    int N, K;
    ll tot[1 << 16];
    int c[MAXN], a[MAXN]; ll sum[MAXN];
    int f[1 << 16];
    
    int main()
    {
        cin>>K>>N;
        for(int i = 0; i < K; i++) c[i] = read();
        for(int i = 1; i <= N; i++) a[i] = read();
        for(int i = 1; i <= N; i++) sum[i] = sum[i - 1] + a[i];
        sum[N + 1] = (1LL << 60); 
        for(int i = 1; i < (1 << K); i++) 
            for(int j = 0; j < K; j++) if(i & (1 << j)) tot[i] += c[j];
    
        memset(f, 0x7f, sizeof(f)); f[0] = 0;
        ll ans = -(1LL << 60);
        for(int state = 0; state < (1 << K); state++) if(f[state] != INF){
            int cur = lower_bound(sum, sum + N + 2, tot[state] - f[state]) - sum;
    
            for(int i = 0; i < K; i++) if(!(state & (1 << i))){
                int tmp = (state | (1 << i));
                int nxt = upper_bound(sum, sum + N + 2, sum[cur] + c[i]) - sum;
                --nxt;
                f[tmp] = min(1LL * f[tmp], f[state] + c[i] - (sum[nxt] - sum[cur]));
                if(nxt == N) ans = max(ans, tot[((1 << K) - 1) ^ tmp]);
            }
        }
        if(ans == -(1LL << 60)) puts("-1");
        else cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    BiliBili, ACFun… And More!【递归算法】
    【VS2015】关于VS2015如何运行的问题
    【打死树莓派】-树莓派3代jessie+Opencv-解决安装不了libgtk2.0-dev包问题
    插入排序2.0
    【C++小白成长撸】--(续)单偶数N阶魔方矩阵
    【C++小白成长撸】--(续)双偶数N阶魔阵
    安装 python-opencv
    二叉树打印
    Kotlin接口
    Kotlin 继承
  • 原文地址:https://www.cnblogs.com/wsmrxc/p/9587949.html
Copyright © 2011-2022 走看看