P2842 纸币问题 1

题目描述

某国有 n n n 种纸币,每种纸币面额为 a i a_i ai 并且有无限张,现在要凑出 w w w 的金额,试问最少用多少张纸币可以凑出来?

输入格式

第一行两个整数 n , w n,w n,w,分别表示纸币的种数和要凑出的金额。

第二行一行 n n n 个以空格隔开的整数 a 1 , a 2 , a 3 , ... a n a_1, a_2, a_3, \dots a_n a1,a2,a3,...an 依次表示这 n n n 种纸币的面额。

输出格式

一行一个整数,表示最少使用的纸币张数。

输入输出样例 #1

输入 #1

复制代码
6 15
1 5 10 20 50 100

输出 #1

复制代码
2

输入输出样例 #2

输入 #2

复制代码
3 15
1 5 11

输出 #2

复制代码
3

说明/提示

对于 40 % 40\% 40% 的数据,满足 n ≤ 10 n\le 10 n≤10, w ≤ 100 w\le 100 w≤100;

对于 100 % 100\% 100% 的数据,满足 1 ≤ n ≤ 10 3 1\le n\le 10^3 1≤n≤103, 1 ≤ a i ≤ w ≤ 10 4 1\le a_i \leq w\le 10^4 1≤ai≤w≤104。

考虑 dp 做法,令 dp[i] 表示凑够 i 元所需的纸币张数。

状态转移方程为 d p [ i ] = m i n ( d p [ i − a [ j ] ] + 1 , d p [ i ] ) dp[i]=min(dp[i-a[j]]+1,dp[i]) dp[i]=min(dp[i−a[j]]+1,dp[i]),其中 a[j] 表示第 j 个面值。

对于从 1 到 w 的每个金额,枚举最后用的面值,以此更新 dp[i] ,最后 dp[w] 即为答案:

c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, w;
    cin >> n >> w;
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    const int INF = 1e9;
    vector<int> dp(w + 1, INF);
    dp[0] = 0;
    
    for (int x = 1; x <= w; ++x) {
        // 枚举最后用的一张纸币
        for (int i = 0; i < n; ++i) {
            if (a[i] <= x && dp[x - a[i]] < INF) {
                dp[x] = min(dp[x], dp[x - a[i]] + 1);
            }
        }
    }
    cout << dp[w] << "\n";
    return 0;
}