《挑战程序设计竞赛》上DP的一道习题。

很裸的多重背包。下面对比一下方法,倍增,优化定义,单调队列。

一开始我写的倍增,把C[i]分解成小于C[i]的2^x和一个余数r。

dp[i][j]的定义前i个数字能否到凑出j来,改成一位滚动数组。

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
//#include<bits/stdc++.h>
using namespace std; #define PS push
#define PB push_back
#define MP make_pair
#define fi first
#define se second const int maxm = 1e5+;
const int maxn = ; bool dp[maxm]; int A[maxn], C[maxn]; int n, m; inline int read()
{
int ret; char c; while(c = getchar(),c<''||c>'');
ret = c-'';
while(c = getchar(),c>=''&&c<='') ret = ret* + c-'';
return ret;
} //#define LOCAL
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif
dp[] = true;
while(scanf("%d%d",&n,&m),n+m){
for(int i = ; i < n; i++)
A[i] = read();
for(int i = ; i < n; i++){
C[i] = read();
}
memset(dp+,,sizeof(bool)*m);
for(int i = ; i < n; i++){
int b = ,vol = A[i];
while((<<b)<=C[i]+){ //11...111 (b) <= C[i] -> 100.... <= C[i]+1
for(int S = m; S >= vol; S--){
dp[S] = dp[S-vol]||dp[S];
}
b++; vol<<=;
}
int r = C[i]-(<<(b-))+;
if(r){
int Vol = A[i]*r;
for(int S = m; S >= Vol; S--){
dp[S] = dp[S-Vol]||dp[S];
}
}
}
int ans = ;
for(int i = ; i <= m; i++) ans += dp[i];
printf("%d\n",ans);
}
return ;
}

binary

复杂度是O(n*m*sigma(logC[i]))。然后果断就TLE了。

再优化只有单调队列了,扎扎并没有想到怎么用单调队列。

书上的解法是优化定义,同样的时间复杂度记录bool信息太浪费了。

dp[i][j]表示前i种凑出面值j时第i种硬币最多的剩余。

核心代码:

        int ans = ;
memset(dp+,-,sizeof(int)*m);
for(int i = ; i < n; i++){
for(int j = ; j <= m; j++){
if(~dp[j]) dp[j] = C[i]; //之前凑出了j
else if(j >= A[i] && dp[j-A[i]]>) { //还可以在凑
dp[j] = dp[j-A[i]] - ;
ans++;
}else dp[j] = -; //凑不出
}
}

跑了1985ms。

另外还发现一件有意思的事情,

当我用一个临时变量数组cnt[j]记录凑出j的最小次数的时候,跑了1235ms。(系统分配似乎更快一点?

        int ans = ;
memset(dp+,,sizeof(bool)*m);
for(int i = ; i < n; i++){
int cnt[maxm] = {};
for(int j = A[i]; j <= m; j++){
if(!dp[j] && dp[j-A[i]] && cnt[j-A[i]] < C[i]){
cnt[j] = cnt[j-A[i]] + ;
ans++;
dp[j] = true;
}
}
}

参考了http://www.cnblogs.com/xinsheng/archive/2013/12/04/3458362.html之后,明白了单调队列的做法的。

总体来说是划分同余类,对于一个同余类用单调队列维护滑动窗口的最小值。(左端为最多减去C[i]个物品的的状态

这道题只要判断存在性,连单调性都用不上(insert不需要删除队尾元素),只要维护滑动窗口的和以及大小就可以了。

但是这道题数据丧心病狂,直接分组常数比较大TLE了。我改成判断0-1和完全才2891 ms飘过(常数写丑了

int ans = ;
memset(dp+,,sizeof(bool)*m);
for(int i = ; i < n; i++){
if(C[i] == ){
for(int j = m; j >= A[i]; j--){
dp[j] = dp[j-A[i]] || dp[j];
}
}
else if(A[i]*C[i] >= m){
for(int j = A[i]; j <= m; j++){
dp[j] = dp[j-A[i]] || dp[j];
}
}
else {
for(int r = ; r < A[i]; r++){
int sum = , hd = , rr = ;
for(int j = r; j <= m; j += A[i]){
if(rr - hd > C[i]){
sum -= q[hd++];
}
sum += q[rr++] = dp[j];
if(sum) dp[j] = true;
}
}
}
}
for(int i = ; i <= m; i++) ans += dp[i];

似乎完全的情况比较多的情况,我只改了一个语句的不同结果。。

dp[j] = dp[j-A[i]] || dp[j]; 2891 ms

if(dp[j-A[i]]) dp[j] = true; 2813 ms

if(dp[j-A[i]] && !dp[j]) dp[j] = true; 2110 ms

POJ 1742 Coins(多重背包,优化)的更多相关文章

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

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

  2. POJ 1742 Coins (多重背包)

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

  3. poj 1742 coins_多重背包

    题意:给你N个种硬币,价值和数量,知道手表不大于m,问能组成(1~m)的价格有多少种情况 套套上次那题的模板直接就行了,http://blog.csdn.net/neng18/article/deta ...

  4. POJ 3260 The Fewest Coins(多重背包+全然背包)

    POJ 3260 The Fewest Coins(多重背包+全然背包) http://poj.org/problem?id=3260 题意: John要去买价值为m的商品. 如今的货币系统有n种货币 ...

  5. hdu 2844 poj 1742 Coins

    hdu 2844 poj 1742 Coins 题目相同,但是时限不同,原本上面的多重背包我初始化为0,f[0] = 1;用位或进行优化,f[i]=1表示可以兑成i,0表示不能. 在poj上运行时间正 ...

  6. poj 1742 Coins(二进制拆分+bitset优化多重背包)

    \(Coins\) \(solution:\) 这道题很短,开门见山,很明显的告诉了读者这是一道多重背包.但是这道题的数据范围很不友好,它不允许我们直接将这一题当做01背包去做.于是我们得想一想优化. ...

  7. poj 1742 Coins(二进制优化多重背包)

    传送门 解题思路 多重背包,二进制优化.就是把每个物品拆分成一堆连续的\(2\)的幂加起来的形式,然后把最后剩下的也当成一个元素.直接类似\(0/1\)背包的跑就行了,时间复杂度\(O(nmlogc) ...

  8. poj 1742 Coins (多重背包)

    http://poj.org/problem?id=1742 n个硬币,面值分别是A1...An,对应的数量分别是C1....Cn.用这些硬币组合起来能得到多少种面值不超过m的方案. 多重背包,不过这 ...

  9. Poj 1742 Coins(多重背包)

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

随机推荐

  1. Ocelot(四)- 认证与授权

    Ocelot(四)- 认证与授权 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/10932805.html 源码地址:http ...

  2. c#中ToString("yyyyMMdd") 与ToString("yyyymmdd")区别

    string a= DateTime.Now.ToString("yyyyMMdd") ; string b=DateTime.Now.ToString("yyyymmd ...

  3. Ckeditor 4 复制粘贴截图并转化base64格式保存至数据库

    虽然Ckeditor 中自带的有上传图片和文件的功能,但是有时候我们并不需要把图片保存至服务器的文件夹中. 反而是截图复制粘贴,把图片转化为base64格式保存在数据库中即可满足要求. 1.首先下载安 ...

  4. thinkphp5加密解密

    thinkphp5目前没有提供加密解密类,但是tp3.2中提供了好几种加密解密方法,我们可以吧3.2的这些类拿来使用. 1.将tp3.2中ThinkPHP\Library\Think的Crypt文件夹 ...

  5. Java基础笔记(十四)——面向对象(续)【构造方法和this关键字】

    一.构造函数的规则 1.构造方法是用来在对象实例化时初始化对象的成员变量的,以完成对象的初始化工作. 2.构造方法与类名相同且没有返回值(返回值也不能为void型).如:public 构造方法名( ) ...

  6. windows驱动环境配置vs2010+wdk7600

    安装wdk后  要勾选集成VSIX选项到vs里面,不然vs里面新建项目时候找不到windows Driver 最简单的方式是安装wdk7600+vs2010+VisualDDK-1.5.7 这三个软件 ...

  7. servlet获取并存储web.xml中context-param参数

    在web.xml中定义了context-param,一般不会随意改动,所以在监听器中做一次处理,容器启动时读取并存储在Properties中,方便以后取值. SysProperties 类用于存储 c ...

  8. MySQL存储过程的动态行转列

    MySQL存储过程中使用动态行转列 最近做项目关于数据报表处理,然而数据库存储格式和报表展现形式不同,需要进行一下行转列的操作,在做上一个项目的时候也看了一下,但是后来换了读取方式,也就没深入研究这个 ...

  9. 影子内存(Shadow RAM)介绍_4

    影子内存(Shadow RAM,或称ROM shadow)是为了提高系统效率而采用的一种专门技术.它把系统主板上的系统ROM BIOS和适配器卡上的视频ROM BIOS等拷贝到系统RAM内存中去运行, ...

  10. 机器学习框架ML.NET学习笔记【2】入门之二元分类

    一.准备样本 接上一篇文章提到的问题:根据一个人的身高.体重来判断一个人的身材是否很好.但我手上没有样本数据,只能伪造一批数据了,伪造的数据比较标准,用来学习还是蛮合适的. 下面是我用来伪造数据的代码 ...