Description

People in Silverland use coins.They have coins of value A1,A2,A3...An Silverland dollar.One day Tony opened his money-box and found there were some coins.He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch. 
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins. 

Input

The input contains several test cases. The first line of each test case contains two integers n(1<=n<=100),m(m<=100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1<=Ai<=100000,1<=Ci<=1000). The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0

Sample Output

8
4

多重背包, 可惜一般多重背包解法不可用

Q: 第二层循环到底是 v 还是余数 d ?

A: 严格分组背包问题的第二层循环是 d, 但也并非完全如此, 第二层分了3个部分, 分别是 01背包, 完全背包, 严格分组背包三种情况

思路:

1. 使用 DP 单调队列求解

2. 分析背包问题的一般解法, 并寻找优化方案

3. 背包问题一般解法的动态规划方程为 dp[i][v] = max(dp[i-1][v-k*w[i]]+k*v[i])

4. 将(3)写的再详细一点, 就是 dp[i][v] = max(dp[i-1][v](不拿), dp[i-1][v-w[i]]+v[i](拿一件), ... dp[i-1][v-k*w[i]]+k*v[i]), 假设 k 是允许拿的最多件数. 关于 k 的取值范围, 首先, k 应该小于 n[i](即第 i 件物品的件数), 其次, k*w[i] < v

5. 举个例子, 对于第 i 件物品, 假设 k == 2, 同时 v 恰好等于 6*w[i], 那么

  dp[i][6*w[i]] = max(dp[i-1][6*w[i]], dp[i-1][5*w[i]]+v[i], dp[i-1][4*w[i]]+2*v[i])

  dp[i][5*w[i]] = max(dp[i-1][5*w[i]], dp[i-1][4*w[i]]+v[i], dp[i-1][3*w[i]]+2*v[i])

  dp[i][4*w[i]] = max(dp[i-1][4*w[i]], dp[i-1][3*w[i]]+v[i], dp[i-1][2*w[i]]+2*v[i])

观察上面三个式子, 发现等号右边有重复的部分, 比如 dp[i-1][4*w[i]] 在三个式子中都出现过, 那么对上式做一下调整

  第一个式子, 右边都减去 6*v[i]

  dp[i][6*w[i]] = max(dp[i-1][6*w[i]]-6*v[i], dp[i-1][5*w[i]]-5*v[i], dp[i-1][4*w[i]]-4*v[i]) + 6*v[i]

  第二个式子, 等号右边减去 5*v[i]

  dp[i][5*w[i]] = max(dp[i-1][5*w[i]]-5*v[i], dp[i-1][4*w[i]]-4*v[i], dp[i-1][3*w[i]]-3*v[i]) + 5*v[i]

  第三个式子, 等号右边减去 4*v[i]

  dp[i][4*w[i]] = max(dp[i-1][4*w[i]], dp[i-1][3*w[i]]-3*v[i], dp[i-1][2*w[i]]+2*v[i]) + 4*v[i]

经过转化, 三个式子右边就出现了部分相同的式子, 相同就意味着可以减少重复计算, 那么, 计算 dp[i][v] 的时候, 可以使用单调队列减少冗余计算, 比如

  开始时, 队列含有 dp[i][4*w[i]] 等号右边三个子式, 求解完 dp[i][4*w[i]], 压缩唯一一个新的子式 dp[i-1][5*w[i]]-5*v[i], 并挤掉 dp[i-1][2*w[i]]+2*v[i], 最后压入 dp[i-1][6*w[i]]-6*v[i], 挤掉 dp[i-1][3*w[i]]-3*v[i], 单调队列能使这个过程的复杂度为 o(1) (dp[i-1][k*w[i]+d] 进入单调队列的次数仅有一次)

6. 再具体一些. v==k*w[i] 的意思是背包的容量恰好是第 i 件物品的 k 倍, 此时 d = v%w[i] = 0. 当 v == k*w[i]+1 时, 即 d == 1, 那么一次遍历可以求解 dp[i][6*w[i]+1], dp[5*w[i]+1], dp[i][4*w[i]]+1]... 可见, 每次遍历能够求解余数相同的那些数

假设 d == v%w[i], d 的取值范围是 [0, w[i]) , 每一项减去的是 v/w[i]

程序的框架可以是

7. 当 w == v 时的一个特例

每次入队(新加入队列)中的元素是 f[v]-(v/w[i])*v[i], 因为 w==v, 那么 f[v]-v+d, 其中 d=v%w[i]

返回的最大值是 队首元素+k/w[i]*v[i] = 队首元素+k-d

针对 1742 这道题, 题目仅要求求解能够覆盖的那些值, 所以题目变得简单一些了

对于 dp[i][k*w[i]+d], 我们仅需判断 dp[i][(k-(0...n[i]))*w[i]+d] 是否有 1  即可, 这有简化为 dp[i][(k-(0...n[i]))*w[i]+d] 的和是否为 0. 不为 0, 则覆盖

总结:

1. 多重背包的一般解法

  <1> 直接解法. dp[i][v] = max(dp[i-1][v-k*w[i]]+k*v[i])

  <2> 转换成01背包. 将一种物品拆分成 1, 2, 4, ...N-2^k-1件. 比如 13就能拆分成 1, 2, 4, 6 件, 然后使用 01 背包的思路求解

2. 单调队列的初始化方法

  <1> st初始化为0, ed 初始化为 -1

  <2> queue[++ed] = dp[v]

  可以减少判断

3. 第 29 行代码 WA 过, v = d, 而不是 v =w[i]

代码:

#include <iostream>
using namespace std; const int MAXN = 150;
int w[MAXN], c[MAXN];
int n, m;
bool dp[100000+10], queue[100000+10]; int solve_dp() {
memset(dp, 0, sizeof(dp));
memset(queue, 0, sizeof(queue));
dp[0] = true; for(int i = 0; i < n; i ++) {
if(c[i] == 1) { // 仅允许一个包, 变成01背包问题
for(int v = m; v >= w[i]; v--) {
if(!dp[v] && dp[v-w[i]])
dp[v] = 1;
}
}else if(c[i]*w[i] >= m) { // 完全背包问题, 即 w[i]*c[i] < m, 放入件数的限制是 c[i]
for(int v = w[i]; v <= m; v++) {
if(!dp[v] && dp[v-w[i]])
dp[v] = 1;
}
}else{ // 严格的分组背包问题
for(int d = 0; d < w[i]; d++) { // 对于所有余数 d [0, w[i])
// 窗口大小为 c[i]
int sum = 0, st = 0, ed = -1; //st,ed 单调队列的开始和结尾, sum 队列中是否有一个 true
for(int v = d; v <= m; v+= w[i]) { // 完全背包 model, 但步长是 w[i]
if(ed - st == c[i]) { // 窗口大小为0, 移除队首元素, 队首后移一位
sum -= queue[st++];
}
queue[++ed] = dp[v];
sum += dp[v];
if(!dp[v] && sum)
dp[v] = 1;
}
}
}
}
int res = 0;
for(int i = 1; i <= m; i ++)
res += dp[i];
return res; }
int main() {
freopen("E:\\Copy\\ACM\\测试用例\\in.txt", "r", stdin);
while(cin >> n >> m && n != 0) {
for(int i = 0; i < n; i ++) {
scanf("%d", &w[i]);
}
for(int i = 0; i < n; i ++) {
scanf("%d", &c[i]);
}
// main function
cout << solve_dp() << endl;
}
return 0;
}

  

Update: 2014年3月14日10:04:41

1. sum = 1 -> dp[v] = 1 优化非常巧妙, 第二次做时依然没想到

2. 分组背包时, 注释写了完全背包 model, 但实际上写成 01 背包 model 也是可以的, 结果与之无关. 但写成 01 背包 model 更加合适, 毕竟分组背包的经典解法是转化为 01 背包

3. 此题和 九度 买卖股票 可以很好做下对比

4. 楼天成是男人就做八题其中一道

POJ 1742 Coins(多重背包, 单调队列)的更多相关文章

  1. POJ 1742 Coins (多重背包)

    Coins Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 28448   Accepted: 9645 Descriptio ...

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

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

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

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

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

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

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

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

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

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

  7. Luogu 3423 [POI 2005]BAN-银行票据 (多重背包单调队列优化 + 方案打印)

    题意: 给出 n 种纸币的面值以及数量,求最少使用多少张纸币能凑成 M 的面额. 细节: 好像是要输出方案,看来很是头疼啊. 分析: 多重背包,裸体??? 咳咳,好吧需要低调,状态就出来了: dp [ ...

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

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

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

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

随机推荐

  1. Linux下安装Python3.6和第三方库

    如果本机安装了python2,尽量不要管他,使用python3运行python脚本就好,因为可能有程序依赖目前的python2环境, 比如yum!!!!! 不要动现有的python2环境! 一.安装p ...

  2. 一个activity

    package com.example.administrator.Activity; import android.content.Context;import android.content.In ...

  3. #include<algorithm>里的函数

    #include<algorithm>里的函数   #include<algorithm>     非修改性序列操作(12个)   循环 对序列中的每个元素执行某操作 for_ ...

  4. python的一些科学计算的包

    在安装numpy这类科学计算的包的时候,pip下载的东西有时候缺少一些东西. 可以到这里下载,根据提示信息,少哪个包,或者哪个包出现错误就安装哪个包. PIL到这里下载

  5. JAVA-JSP表达式

    相关资料: <21天学通Java Web开发> 结果总结: 1.<%= %>JSP表达式中的代码会首先执行,然后转换成字符串显示到网页上.2.JSP表达式标签不必也不能使用分号 ...

  6. IOS 使用XIB 自定义View

    一般自定义View       代码方式 有 在初始化的时候添加 子Views - (id)initWithFrame:(CGRect)frame { self = [super initWithFr ...

  7. 【C#/WPF】TextBlock/TextBox/Label编辑文字的问题

    标题有点描述不清,就当是为了方便自己用时易于搜索到. 总之需求是:显示用户信息(文字)时,允许用户编辑自己的信息. 效果图如下: 点击[编辑]按钮前: 点击[编辑]按钮后,允许编辑: 别吐槽为甚性别还 ...

  8. kubernetes daahboard权限限制

    dashboard在多人使用的时候经常遇到误操作的情况,为了对dashboard进行限制,对dashboard进行了权限控制, 这里主要限制只允许pod被删除.1:创建对应权限的ClusterRole ...

  9. Linux远程复制文件

    将本机文件app.properties 复制到用户为root,ip为ip的具体路径下去 scp app.properties root@ip:/apps/javaconf/common/ 其他参考: ...

  10. 决策树---ID3算法(介绍及Python实现)

    决策树---ID3算法   决策树: 以天气数据库的训练数据为例. Outlook Temperature Humidity Windy PlayGolf? sunny 85 85 FALSE no ...