题意:

给出 n 种纸币的面值以及数量,求最少使用多少张纸币能凑成 M 的面额。

细节:

好像是要输出方案,看来很是头疼啊。

分析:

多重背包,裸体???

咳咳,好吧需要低调,状态就出来了: dp [ i ] 表示面额为 i 最少需要多少张纸币组成。

转移:dp [ i ] = min ( dp [ i ] , dp [ i - w [ j ] × k ] + k ) ( k 表示当前纸币有几张,0 ≤ k ≤ num [ j ] 表示这类纸币的数量)

好了你就完成了此题的部分分,本人蒟蒻打死都想不到背包还能优化可是它显然可以。

所以开始向满分冲刺,对于上方的转移你显然不能优化,对于一个转移竟然跟三个变量有关,嘿嘿嘿,这个转移很有心机,

我们需要进行方程的转换,不妨先来看一下是否存在重复,假设当前 num [ j ] = 2w [ j ] = 1 时:

i = 3 时,可以从 3、2、1 进行转移

i = 4 时,可以从 4、3、2 进行转移

i = 5 时,可以从 5、4、3 进行转移

顿时豁然开朗,存在重复存在优化的可能性,但是却对应了不同的价值,比如 i = 3 时从 2 进行转移的花费为 1 ,但是对于 i = 4 来说花费就变成了 2 ,所以对于当前的第 j 层来说其转移的值是不定的,所以无法进行优化。

但如果先在转移之前每个值都减去 i 的话,就会出现这样的情况:

i = 3 时,从 dp [ 1 ] - 1 + 3dp [ 2 ] - 2 + 3dp [ 3 ] - 3 + 3 转移

i = 4 时,从 dp [ 2 ] - 2 + 4dp [ 3 ] - 3 + 4dp [ 4 ] - 4 + 4 转移

不然发现对于 i 来说其都加了 i ,但前方的转移都是相同的,这样就可以开始进一步的优化了。

不妨令 d = v [ i ],a = j / d,b = j % d ,即 j = a × d + b

这样方程就变化成了:dp [ j ] = min ( dp [ b + k × d] - k )+ a

所以只需要维护一个关于 dp [ b + k × d ] - k 单调递增的队列即可。

最后我们再来考虑一下如何打印出最后的方案,其实只需要记录每个转移是从哪一个面额而来的以及它当前用了哪一种面额的纸币即可,且这只是其中的一种方案。

代码:

#include<bits/stdc++.h>
#define MAXN 20005
using namespace std; struct Node{
int ans,W;
}que[MAXN];
int n, m, w[MAXN], num[MAXN], f[MAXN], d[205][MAXN]; void print(int x, int y){ //打印方案
if (!x) return;
print(x-1, d[x][y]);
printf("%d ", (y-d[x][y])/w[x]);
} int main(){
scanf("%d", &n);
for (int i=1; i<=n; i++) scanf("%d", &w[i]);
for (int i=1; i<=n; i++) scanf("%d", &num[i]);
scanf("%d", &m);
memset(f, 0x3f3f3f, sizeof f);
f[0]=0;
for (int i=1; i<=n; i++){
for (int j=0; j<w[i]; j++){
int head=1, tail=0;
for (int k=j, cnt=0; k<=m; cnt++, k+=w[i]){
if (tail-head==num[i]) head++; //维护队首
int Ans=f[k]-cnt;
while (head<=tail && Ans<que[tail].ans) --tail; //维护队尾
que[++tail].ans=Ans, que[tail].W=k; //加入新的元素
f[k]=que[head].ans+cnt; //转移当前的最有状态
d[i][k]=que[head].W; //记录当前的最有转移从何而来
}
}
}
printf("%d\n", f[m]);
print(n, m); //打印方案
return 0;
}

小结:

可以把该类问题进行一般化的处理,也就是将方程转化为 dp [ i ] = min ( dp [ i ] , dp [ i - w [ j ] × k ] + k × v [ i ] ) 同理它可以转化为以下的式子:

假设 d = v [ i ],a = j / d,b = j % d,即 j = a × d + b

dp [ i ][ j ] = max { dp [ i - 1 ] [ b + k × d ] - k × w[i] } + a × w[i] (a – m[i] <= k <= a)

考虑用单调队列优化即可。Q A Q

Luogu 3423 [POI 2005]BAN-银行票据 (多重背包单调队列优化 + 方案打印)的更多相关文章

  1. [BZOJ4182]Shopping (点分治+树上多重背包+单调队列优化)

    [BZOJ4182]Shopping (点分治+树上多重背包+单调队列优化) 题面 马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街.商店街有n个商店,并且它们之间的道路构成了一颗树 ...

  2. 【POJ1276】Cash Machine(多重背包单调队列优化)

    大神博客转载http://www.cppblog.com/MatoNo1/archive/2011/07/05/150231.aspx多重背包的单调队列初中就知道了但一直没(不会)写二进制优化初中就写 ...

  3. hdu 2844 多重背包+单调队列优化

    思路:把价值看做体积,而价值的大小还是其本身,那么只需判断1-m中的每个状态最大是否为自己,是就+1: #include<iostream> #include<algorithm&g ...

  4. poj1742 Coins(多重背包+单调队列优化)

    /* 这题卡常数.... 二进制优化或者单调队列会被卡 必须+上个特判才能过QAQ 单调队列维护之前的钱数有几个能拼出来的 循环的时候以钱数为步长 如果队列超过c[i]就说明队头的不能再用了 拿出来 ...

  5. BZOJ.4182.Shopping(点分治/dsu on tree 树形依赖背包 多重背包 单调队列)

    BZOJ 题目的限制即:给定一棵树,只能任选一个连通块然后做背包,且每个点上的物品至少取一个.求花费为\(m\)时最大价值. 令\(f[i][j]\)表示在点\(i\),已用体积为\(j\)的最大价值 ...

  6. bzoj 1531 Bank notes 多重背包/单调队列

    多重背包二进制优化终于写了一次,注意j的边界条件啊,疯狂RE(还是自己太菜了啊啊)最辣的辣鸡 #include<bits/stdc++.h> using namespace std; in ...

  7. POJ 1742 Coins(多重背包, 单调队列)

    Description People in Silverland use coins.They have coins of value A1,A2,A3...An Silverland dollar. ...

  8. 多重背包 /// 单调队列DP oj1943

    题目大意: em.... 就是多重背包 挑战340页的东西 ...自己的笔记总结总是比较乱的 重点:原始的状态转移方程中 更新第 i 种物品时 重量%w[i] 的值不同 则它们之间是相互独立的: 1- ...

  9. Luogu 2569 [SCOI2010]股票交易 (朴素动规转移 + 单调队列优化)

    题意: 已知未来 N 天的股票走势,第 i 天最多买进 as [ i ] 股每股 ap [ i ] 元,最多卖出 bs [ i ] 股每股 bp [ i ] 元,且每天最多拥有 Mp 股,且每两次交易 ...

随机推荐

  1. GYM 101889I(mst+lca)

    最小生成树上倍增询问裸的. const int maxn = 2e5 + 5; int n, m, q; //图 struct Edge { int u, v; ll cost; bool opera ...

  2. C#私有的构造函数的作用

    C#私有的构造函数的作用:当类的构造函数是私有的时候,也已防止C1 c1=new C1();实例化类.常见的应用是工具类和单例模式. using System;using System.Collect ...

  3. Spring Boot启动过程源码分析--转

    https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...

  4. C#打开office文件,pdf文件和视频文件

    打开office文件 1 需要从网站下载dsoframer.ocx文件 2 把dsoframer.ocx文件复制到c:\windows\system32目录下 开始->运行->regsvr ...

  5. 如何尽量避免引用jQuery

    Introduction 正如jQuery所宣称的一样,Write Less, Do More.很多时候我们喜欢用它来解决问题.但增加一个库必然意味着更大的网络负担,意味着更高的页面初始载入时间.并且 ...

  6. 基于Java实现的插入排序算法

    简述 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常 ...

  7. 在CentOS 6.7 64位安装PHP的PDO_OCI扩展 Installing PDO_OCI extension on CentOS 6.7 64bit

    需求&背景 最近根据项目需求,要在php中远程连接Oracel 11g Express数据库,为了开发方便,决定采用pdo,也就是php的PDO_OCI扩展,但是php安装的时候并没有安装PD ...

  8. Caused by: javax.el.PropertyNotFoundException: Property 'title' not found on type java.lang.String

    问题:在JSP页面显示从后台传过来的list集合数据报错. 错误信息: Caused by: javax.el.PropertyNotFoundException: Property 'title' ...

  9. HDU 1693 Eat the Trees (插头DP)

    题意:给一个n*m的矩阵,为1时代表空格子,为0时代表障碍格子,问如果不经过障碍格子,可以画一至多个圆的话,有多少种方案?(n<12,m<12) 思路: 这题不需要用到最小表示法以及括号表 ...

  10. hihoCoder #1068 : RMQ-ST算法(模板)

    AC G++ 826ms 146MB 思路: 时间复杂度O(nlogn). //#include <bits/stdc++.h> #include <iostream> #in ...