博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POJ1742 Coins(男人八题之一)
阅读量:5241 次
发布时间:2019-06-14

本文共 1934 字,大约阅读时间需要 6 分钟。

前言

大名鼎鼎的男人八题,终于见识了...

题面

分析

§ 1 多重背包

这很显然是一个完全背包问题,考虑转移方程:

DP[i][j]表示用前i种硬币能否取到金额j,ture表示可以,false表示不行。

则有

DP[i][j] = DP[i - 1][j] | DP[i - 1][j - k * Ai], 0 ≤ k ≤ Ci, j - k * Ai ≥ 0

这是一个O(N3)的算法,考虑到数据范围1 ≤ N ≤ 100, M ≤ 100000, 1 ≤ Ci≤ 1000,显然会超时。

§ 2 优化

 考虑上面的转移方程,每个方程只记录了可行解的存在与否;而事实上,当前第i种硬币的剩余数也是一个状态,而此前的方程关于这个状态是靠k来枚举。

我们可以考虑一下完全背包和多重背包的异处,正是多了一个物品数量的限制,才使我们的枚举增加了一个维度,导致时间复杂度增加;而我们同时还要记录当前状态可达到的最大价值(普通多重背包),因而枚举选的物品数量的循环时必不可少的。但是,在这个问题中,我们只需要判断可行解的存在性。因此,我们可以用DP[i][j]来记录用前i种硬币,在取到金额j的情况下,第i种硬币剩余的最大数量。(不能用-1表示)

则有

DP[i][j] =

  • C[i], DP[i - 1][j] ≥ 0(如果前(i-1)种硬币已经足以凑出这个金额j,那么就根本用不着第种硬币,因此全部剩下,也就是Ci
  • -1, DP[i][j - A[i]] ≤ 0 || j < A[i](若前面凑更小的面额时已经用尽第i种硬币,或者当前硬币的面额太大了,那么就凑不出来,即-1)
  • DP[i][j - A[i]] - 1

注意这些条件时依次判断的。

(如果以上看了仍然不懂的同学,可以去看这个dalao写的详细推理过程 )

因而,时间复杂度就被降到了O(N2)。

还有很重要的一点,之间建N * M的数组是会超空间的,因此,要使用滚动数组。

§ 3 总结

事实上,这题降维就是考的只判断解是否可行的条件。一般来说,这种思路很难想到,很少人会去想写这样一个多重背包的方程,但是,正是这个问题独特的性质,使得它可行。

而且这个问题还有一种用单调队列的写法,目前还没有弄懂,不过也是多重背包的一大利器。

§ 4 参考代码

// POJ1742// Coins// LouTiancheng@POJ#include 
#include
#include
const int MAXN = 105;const int MAXM = 100005;int N, M, DP[2][MAXM] = {
0}, A[MAXN], C[MAXN], i, j;void solve();int main() { while (scanf("%d%d", &N, &M) == 2) { if (N == 0 || M == 0) break; solve(); } return 0;}void solve() { for (i = 0; i < N; i++) scanf("%d", &A[i]); for (i = 0; i < N; i++) scanf("%d", &C[i]); int *prv = DP[0], *nxt = DP[1]; memset(prv + 1, -1, sizeof(int) * M); prv[0] = 0; for (i = 0; i < N; i++) { for (j = 0; j <= M; j++) if (prv[j] >= 0) nxt[j] = C[i]; else if (j < A[i] || nxt[j - A[i]] <= 0) nxt[j] = -1; else nxt[j] = nxt[j - A[i]] - 1; std::swap(prv, nxt); } int ans = 0; for (i = 1; i <= M; i++) if (prv[i] >= 0) ans++; printf("%d\n", ans);}

另外,有问题的童鞋欢迎提问~

谢谢大家!

转载于:https://www.cnblogs.com/CaptainSlow/p/9245369.html

你可能感兴趣的文章
Springboot-配置文件
查看>>
Springboot-日志框架
查看>>
P1192-台阶问题
查看>>
一、使用pip安装Python包
查看>>
spring与quartz整合
查看>>
Kattis之旅——Eight Queens
查看>>
3.PHP 教程_PHP 语法
查看>>
Duilib扩展《01》— 双击、右键消息扩展
查看>>
利用Fiddler拦截接口请求并篡改数据
查看>>
python习题:unittest参数化-数据从文件或excel中读取
查看>>
Android控件之GridView探究
查看>>
在工程中要加入新的错误弹出方法
查看>>
PS 滤镜— — sparkle 效果
查看>>
snmpwalk命令常用方法总结
查看>>
网站产品设计
查看>>
代理ARP
查看>>
go 学习笔记(4) ---项目结构
查看>>
java中静态代码块的用法 static用法详解
查看>>
Java线程面试题
查看>>
Paper Reading: Relation Networks for Object Detection
查看>>