sincerit 硬币找零之种类数

现存在一堆面值为 1,2,5,10,20,50 面值的硬币,问给你一个纸币是m,求出把m纸币换成硬币的种类数
思路是动态规划
假设有n种硬币,纸币是m
dp[i][j] 表示前i种硬币能把纸币换成硬币的种类数
转移方程 对于第i种硬币 dp[i][j] = dp[i-1][j] + dp[i][j-coin[i]];
初始条件 dp[0~n][0] = 1; 这里恰好能找零的一个方案 比如面值为5的硬币和面值为5的纸币 dp[i][5-5] = 1
dp[0][1~m] = 0, 硬币种类都没有自然没法找零

转移方程的具体解释:
对于某种面值的硬币,要么使用了(可能使用多次)它,要么不使用它。故:
dp[i,j]=dp[i-1,j] + dp[i,j-coin[i]]
dp[i-1,j] 表示不使用第 i 枚硬币, dp[i, j-coin[i]] 表示至少使用了一次 第 i 枚硬币。dp[i, j-coins[i]] 表示,第 i 枚硬币还可以继续使用,所以参数是i而不是i-1

#include <stdio.h>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int dp[100][100];
// 表示的含义 dp[i][j]表示前i种面额能找零面值为j的种类数
// 转移方程 对于第i种面额 dp[i][j] = dp[i-1][j] + dp[i][j-coin[i]];
// 初始条件 dp[0~n][0] = 1表示恰好有一种找零的方案, dp[0][1~m] = 0表示不存在方案 
int main() {
  int n, m; // n表示钱的种类, m表示钱数
  int coin[10];
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) scanf("%d", &coin[i]);
  for (int i = 0; i <= n; i++) dp[i][0] = 1;
  for (int j = 1; j <= m; j++) dp[0][j] = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (j >= coin[i]) dp[i][j] = dp[i-1][j] + dp[i][j-coin[i]];
      else dp[i][j] = dp[i-1][j];
    }
  }
  printf("%d\n", dp[n][m]);
  return 0;
}

然后根据背包九讲里大神的解释在空间上优化一下
背包九讲-01背包:https://blog.csdn.net/witnessai1/article/details/52702754 主要看里面的优化方法,还有另一些不同的提问方式的解法

这道题是完全背包

#include <stdio.h>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int dp[100];
// 表示的含义 dp[i][j]表示前i种面额能找零面值为j的种类数
// 转移方程 对于第i种面额 dp[i][j] = dp[i-1][j] + dp[i][j-coin[i]];
// 初始条件 dp[0~n][0] = 1表示恰好有一种找零的方案, dp[0][1~m] = 0表示不存在方案 
int main() {
  int n, m; // n表示钱的种类, m表示钱数
  int coin[10];
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) scanf("%d", &coin[i]);
  memset(dp, 0, sizeof(dp));
  dp[0] = 1; 
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) { // 完全背包从小到大, 01背包从大到小
      if (j >= coin[i]) dp[j] += dp[j-coin[i]];
    }
  }
  printf("%d\n", dp[m]);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/sincerit/article/details/84452214