状压\(DP\) + 二分

考虑构成:\(k<=16\)所以根据\(k\)构造状压\(dp\),将所有硬币的使用情况进行状态压缩

考虑状态:数组\(dp[i]\)表示用\(i\)状态下的硬币可以购买到第几个商品 ,\(f[i]\)表示状态\(i\)下的花费

考虑转移:使用当前硬币的状态一定由使用上一个硬币的状态转移而来

举个例子:之前状态\(x\):\(dp[x] = y\), \(i = 2 = (010)_2\) ,当前枚举到的状态\(i = 3 = (011)_2\) , \(dp[i] = (dp[x] + 1\)开始能买到哪里\((<=n))\), 相当于状态\(x\)能购买到\(y\)号物品,\(i\)要从\(y+1\)号开始购买 ,\(i\)状态比\(x\)状态在二进制的第三位多了1,说明比i状态多用了一个编号为1的硬币,\(f[i] = f[x]\) + 硬币\(1\)的价值
状态转移完成

考虑具体做法:外层循环枚举所有状态,内层循环枚举每一位,若当前状态\(i\)的第\(j\)位为\(1\),则可以进行转移

然后可以进行枚举\(n\)件物品,考虑每一件是否可以购买,一直到不能购买为止

因为一种状态可以被更新多次,所以要取\(max\),保证\(dp\)数组存的是能买到的最大编号,然后更新\(dp\)数组和\(f\)数组

如果到第\(n\)件都可以买,则可以购买全部物品,\(ans\)记录当前的最小花费,最后用所有硬币的总面值减去最小花费即为答案

如果\(ans\)没有被更新过,说明不能购买,输出\(-1\)

时间复杂度\(O(2^kkn)\),超时

考虑优化:发现每次枚举物品统计价值来检查是否能够购买是冗余操作,可以用前缀和预处理一下,然后每次检查的时候进行一次二分就可以了

时间复杂度\(O(2^kklogn)\),可通过本题

考虑正确性:因为外层循环枚举状态是从小到大枚举,所以保证当前状态某一位少\(1\)(即当前使用硬币数减一)的状态已经被转移过了

注意事项
1.不要错误理解题意,注意每次支付只能支付一枚硬币 ,不能算把硬币凑出来的总钱数然后判断能购买多少,这种错误做法能拿到\(93\)分的好成绩(大雾)是因为数据太水

2.二分的时候注意初始的左端点,因为从使用当前硬币的状态转移过来,所以要从使用当前硬币前状态所能购买到的物品\(+1\)作为左端点进行二分,右端点不会变化一直是\(n\)

3.因为二分时要检查的值要与前缀和数组进行比较,所以比较时前缀和数组应该减去左端点之前的前缀

4.注意二分的边界问题以及最后的返回值

代码:

#include <cstdio>
#include <cctype>
#define min(a, b) a < b ? a : b
#define MAXN 100001
#define N 17

int n, m, tot_money, ans = 2147483647;
int dp[1 << N], f[1 << N], sum[MAXN], pay[MAXN], coin[MAXN];

inline int read() {
    int s = 1, w = 0; char ch = getchar();
    for(; ! isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = w * 10 + ch - '0';
    return s * w;
}
inline int check(int x, int cha) {
    int l = cha, r = n, mid;
    while(l <= r) {
      mid = (l + r) >> 1;
      if(sum[mid] - sum[cha - 1] == x) return mid;
      if(sum[mid] - sum[cha - 1] < x) l = mid + 1;
      else r = mid - 1;
    }
    return r;
}

int main() {
    m = read(), n = read();
    for(int i = 1; i <= m; i ++) coin[i] = read(), tot_money += coin[i];
    for(int i = 1; i <= n; i ++) pay[i] = read(), sum[i] = sum[i - 1] + pay[i];
    for(int i = 1; i < (1 << m); i ++) {
      for(int j = 0; j < m; j ++) if(i & (1 << j)) {
        int x = (i ^ (1 << j)), sum;
      if((sum = check(coin[j + 1], dp[x] + 1)) > dp[i])
        dp[i] = sum, f[i] = f[x] + coin[j + 1];
          if(dp[i] == n) ans = min(f[i], ans);
      }
    }
    printf("%d", (tot_money - ans) < 0 ? -1 : tot_money - ans);
    return 0;
} 

[洛谷P3092]【[USACO13NOV]没有找零No Change】的更多相关文章

  1. 洛谷P3092 [USACO13NOV]没有找零No Change

    P3092 [USACO13NOV]没有找零No Change 题目描述 Farmer John is at the market to purchase supplies for his farm. ...

  2. 洛谷 P3092 [USACO13NOV]没有找零No Change

    题目描述 Farmer John is at the market to purchase supplies for his farm. He has in his pocket K coins (1 ...

  3. P3092 [USACO13NOV]没有找零No Change

    题目描述 Farmer John is at the market to purchase supplies for his farm. He has in his pocket K coins (1 ...

  4. luogu P3092 [USACO13NOV]没有找零No Change

    题目描述 Farmer John is at the market to purchase supplies for his farm. He has in his pocket K coins (1 ...

  5. Luogu P3092 [USACO13NOV]没有找零No Change【状压/二分】By cellur925

    题目传送门 可能是我退役/NOIP前做的最后一道状压... 题目大意:给你\(k\)个硬币,FJ想按顺序买\(n\)个物品,但是不能找零,问你最后最多剩下多少钱. 注意到\(k<=16\),提示 ...

  6. P3092 [USACO13NOV]没有找零No Change 状压dp

    这个题有点意思,其实不是特别难,但是不太好想...中间用二分找最大的可买长度就行了. 题干: 题目描述 Farmer John <= K <= ), each with value .., ...

  7. [USACO13NOV]没有找零No Change [TPLY]

    [USACO13NOV]没有找零No Change 题目链接 https://www.luogu.org/problemnew/show/3092 做题背景 FJ不是一个合格的消费者,不知法懂法用法, ...

  8. 【[USACO13NOV]没有找零No Change】

    其实我是点单调队列的标签进来的,之后看着题就懵逼了 于是就去题解里一翻,发现楼上楼下的题解说的都好有道理, f[j]表示一个再使用一个硬币就能到达i的某个之前状态,b[now]表示使用那个能使状态j变 ...

  9. [luoguP3092] [USACO13NOV]没有找零No Change(状压DP + 二分)

    传送门 先通过二分预处理出来,每个硬币在每个商品处最多能往后买多少个商品 直接状压DP即可 f[i]就为,所有比状态i少一个硬币j的状态所能达到的最远距离,在加上硬币j在当前位置所能达到的距离,所有的 ...

随机推荐

  1. TCP/UDP的网络底层实现

    1.1Socket的使用背景 当我们在使用微信.玩游戏.收发邮件,以及用web浏览器上网时,底层的实现是TCP/UDP的协议,封装socket实现网络通信功能. 了解了网络通信的底层实现原理,在出现s ...

  2. VS2019+EF6连接Mysql

    开发环境:Win10 + VS2019Mysql服务器版本:8.0.16 一.下载并安装插件(必备) MySQL-Connector-net-6.9.12  链接https://cdn.mysql.c ...

  3. 读《计算机系统要素:从零开始构建现代计算机》的思考:CodeGen

    掌握目标语言的使用.编写 是非常重要的!!! 如果你要实现的Jack语言编译器是把Jack语言代码编译成虚拟机VM代码.或者直接成汇编代码,要完成源代码中unit A——> 目标语言代码转写此u ...

  4. 并发编程--一堆锁,GIL,同步异步,Event事件

    目录 一堆锁 死锁现象(*****) 递归锁 RLock (了解) 信号量 (了解) GIL(*****) 什么时GIL锁 为什么需要GIL锁 Cpython解释器与GC的问题 GIL锁带来的问题 多 ...

  5. 35、element ui tab切换加载echarts不显示或显示不全问题解决:

    <el-tabs v-model="activeName" @tab-click="handleClick" type="border-card ...

  6. java并发值多线程同步业务场景以及解决方案

    1.20个人排队同时访问2个购票窗口,同时能购票的只有两个人,当其中一个人买票完成后,18个人中的其中一个在占用窗口进行购买. 20个人相当于20个线程,2相当于资源,当18个人等待的时候,相当于线程 ...

  7. 不知道密码情况下 进行docker数据库可视化连接

    1. 通过命令 docker inpect mysql容器id ,查询mysql容器的密码和绑定的端口号 2.通过vscode插件或者navicat等可视化工具,进行连接即可. 有问题请进群联系我,或 ...

  8. 自定义View(五),onLayout

    转自http://blog.csdn.net/a396901990/article/details/38129669 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量——onM ...

  9. 环境搭建:添加 xgboost 到 Anaconda

    原文参考:https://blog.csdn.net/lvsehaiyang1993/article/details/80619495 原文博主:Big_quant

  10. UGUI和NGUI的优化分享

    学习资料 来自UWA的分享,针对于Unity 4.x 及5.3 以下版本,Unity5.5及更高版本可能适用. 文章:UWA技术直播视频集锦 UGUI &NGUI http://blog.uw ...