POJ1742 Coins(男人八题之一)

前言

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

题面

http://poj.org/problem?id=1742

分析

§ 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写的详细推理过程 http://www.hankcs.com/program/cpp/poj-1742-coins.html

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

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

§ 3 总结

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

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

§ 4 参考代码

// POJ1742
// Coins
// [email protected]
#include <cstdio>
#include <cstring>
#include <algorithm>
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

时间: 2024-11-08 05:58:34

POJ1742 Coins(男人八题之一)的相关文章

LCT男人八题系列

楼教的男人八题名气甚大,今天做了一道感觉还是涨了不少姿势的,然而估计之后的每道题都要看题解吧,姑且先记录一下.以后再做再更 1737 Connected Graph 1100 [email protected] 1738 An old Stone Game 407 [email protected] 1739 Tony's Tour 671 [email protected] 1740 A New Stone Game 2240 [email protected] 1741 Tree 1728

poj 1741 楼教主男人八题之一:树分治

http://poj.org/problem?id=1741 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid

poj 1743 Musical Theme(男人八题&amp;后缀数组第一题)

Musical Theme Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 17298   Accepted: 5939 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the

男人八题2019

打的第三年男人八题了= = 感觉自己可能能创造一个EZ历史上打过最多男人八题的人(嘤嘤嘤我明明是妹子啊 考场上是5题 目前补了7题 目录 Biology Chemistry Chinese English Geography History Math Physics Biology 模拟= = //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cm

poj1737~poj1744——ltc男人八题

准备开刷这充满神秘感的八道题,虽然我不是男的... 完成度:3/8 2016.7.~?   poj1742 Coins 题意:给你n种面值的硬币和每种硬币的数量.求1~m中有多少个可以用这些硬币组成. 算法:dp 解析:dp[i][j]表示用前i中硬币组成j时第i种硬币剩余的枚数(若不能组成j则为-1) 考虑以下四种情况: (1)若dp[i-1][j]>=0,则无需第i种硬币,为c[i] (2)若val[i]>j,面值太大,则为-1 (3)若dp[i][j-val[i]]<=0,无法组成

POJ 1741 男人八题——树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 23829   Accepted: 7900 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an

hdu 2844 Coins 多重背包模板题 ,二进制优化。据说是楼教主的男人八题之一

Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8052    Accepted Submission(s): 3291 Problem Description Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One

新男人八题---AStringGame

终于完成进度男人1/8,为了这题学了sam= = 题意先有一个串,n个子串,两个人轮流每次在子串上加字符,要求加完后还是原串的子串,最后不能加的就是输者,求赢的人 解法:sam之后在构造的状态图上跑sg函数,这题的sg状态不会超过26,所以直接枚举所有状态即可,然后记忆化搜索求sg函数,最后异或起来就是答案了 //#pragma comment(linker, "/stack:200000000") //#pragma GCC optimize("Ofast,no-stack

男人八题_POJ-1741

题目链接:http://poj.org/problem?id=1741 树分治相关论文:http://wenku.baidu.com/view/60c6aa1ffc4ffe473368aba8.html 由于论文中有该题的讲解,所以便不再赘述.代码如下 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std;