PAT甲级 1068 Find More Coins (30 分)

题目:戳这里

题意:
给定n个硬币和各个硬币的面值,购买价值为m的货物,问是否能刚好买下。
也就是从n个数中取若干个数相加等于m。
要求最终的答案字典序越小越好。

解题思路:
这题我是用01背包+记录路径的方法,之前写得少,所以写了很久。

首先我用二维数组pre[][]求01背包,这样每一步的状态都能记录下来。
注意: 在这里我建议pre数组用来记录当前选取了哪个硬币,之前我记录的是当前的状态,但是过不了我自己出的一个样例(样例我写在代码后面的注释里),后来把每一步的状态打表出来,才想起来若记录状态,则pre[][]的状态可以是最开始的状态顺延下来的,而不是刚好由前一步状态推出来。(说的有点绕,但我相信如果有人记录的也是状态的话,也会遇到这种情况)
然后,将pre数组初始化为inf,并将最开始的状态pre[][0]全部初始化为0,这样初始化的好处是,如果pre[1][m]的值是inf,则可以直接输出No
Solution。 接着进行常规的01背包,求完后,根据最终的状态和数组中记录的 第几个硬币 的值一步一步往前推,dfs遍历输出。

第二天我又仔细想想,其实这就是个裸的01背包。。。前面那么多复杂的心理活动无非是因为我01背包学的不扎实罢了。用二维数组求01背包,数组中会每一步的状态都会记录下来,直接搜索状态输出即可。

代码(注释中有样例):

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
const int maxe = 1e2+10;
const int inf = 0x3f3f3f3f;
typedef long long ll;
int nu[maxn];
int dp[maxn];
int pre[maxn][maxe];
int n, m;
void dfs(int i, int j) {
    if(j == 0) return;
    printf(" %d", nu[pre[i][j]]);
    dfs(pre[i][j] + 1, j - nu[pre[i][j]]);//搜索上一步的状态
}
int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", nu+i);
    }
    sort(nu + 1, nu + 1 + n);
    memset(pre, inf, sizeof(pre));
    for(int i = 1; i <= n+1; ++i) {
        pre[i][0] = 0;
    }
    for(int i = n; i >= 1; --i) {
        for(int j = m; j >= nu[i]; --j) {
            if(pre[i+1][j - nu[i]] != inf) {
                pre[i][j] = i;
            }
            if(pre[i][j] == inf) {//若这一步没对其进行修改,则将上一步的状态顺延下来
                pre[i][j] = pre[i+1][j];
            }
        }
    }
    if(pre[1][m] > m) {
        puts("No Solution");
    } else {
        printf("%d", nu[pre[1][m]]);
        dfs(pre[1][m] + 1, m - nu[pre[1][m]]);
    }
    return 0;
}

/*
4 7
1 2 3 5
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值