题目
题目链接
解题思路
-
问题分析:
- 每种面值的货币可以使用任意张(完全背包特征)
- 目标是找到组成 \(aim\) 的最少货币数
- 无法组成时返回 -1
-
动态规划设计:
- 状态定义:\(dp[i]\) 表示组成金额 \(i\) 所需的最少货币数
- 初始化:\(dp[0]=0\),其他为无穷大
- 状态转移:\(dp[j] = \min(dp[j], dp[j-coin] + 1)\)
- 其中 \(coin\) 是当前面值
-
优化:
- 使用一维 \(dp\) 数组(滚动数组)
- 正序遍历(完全背包特征)
代码
#include <iostream>
#include <vector>
#include <climits>
using namespace std;int minCoins(vector<int>& arr, int aim) {if(aim == 0) return 0;vector<int> dp(aim + 1, INT_MAX);dp[0] = 0;// 遍历每种面值for(int coin : arr) {// 完全背包正序遍历for(int j = coin; j <= aim; j++) {// 只有当dp[j-coin]不是无穷大时才更新if(dp[j-coin] != INT_MAX) {dp[j] = min(dp[j], dp[j-coin] + 1);}}}return dp[aim] == INT_MAX ? -1 : dp[aim];
}int main() {int n, aim;cin >> n >> aim;vector<int> arr(n);for(int i = 0; i < n; i++) {cin >> arr[i];}cout << minCoins(arr, aim) << endl;return 0;
}
import java.util.*;public class Main {public static int minCoins(int[] arr, int aim) {if(aim == 0) return 0;int[] dp = new int[aim + 1];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;// 遍历每种面值for(int coin : arr) {// 完全背包正序遍历for(int j = coin; j <= aim; j++) {// 只有当dp[j-coin]不是无穷大时才更新if(dp[j-coin] != Integer.MAX_VALUE) {dp[j] = Math.min(dp[j], dp[j-coin] + 1);}}}return dp[aim] == Integer.MAX_VALUE ? -1 : dp[aim];}public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int aim = sc.nextInt();int[] arr = new int[n];for(int i = 0; i < n; i++) {arr[i] = sc.nextInt();}System.out.println(minCoins(arr, aim));sc.close();}
}
def min_coins(arr: list, aim: int) -> int:if aim == 0:return 0dp = [float('inf')] * (aim + 1)dp[0] = 0# 遍历每种面值for coin in arr:# 完全背包正序遍历for j in range(coin, aim + 1):# 只有当dp[j-coin]不是无穷大时才更新if dp[j-coin] != float('inf'):dp[j] = min(dp[j], dp[j-coin] + 1)return dp[aim] if dp[aim] != float('inf') else -1if __name__ == "__main__":n, aim = map(int, input().split())arr = list(map(int, input().split()))print(min_coins(arr, aim))
算法及复杂度
- 算法:动态规划(完全背包)
- 时间复杂度:\(\mathcal{O}(n \times aim)\),\(n\) 是货币种类数,\(aim\) 是目标金额
- 空间复杂度:\(\mathcal{O}(aim)\),使用一维 \(dp\) 数组