1. 程式人生 > >sincerit 硬幣找零之種類數

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;
}