题型1:求总数
1角 2角 5角硬币有无限个,给定一个money值,求由可以兑换的硬币的组合数
思路: 完全背包的变体。
dp[i][j]表示前i个硬币且要兑换的钱数为j时的总的组合数, 假设money = 6,具体情况如下:
表头 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | 1 | 1 | 2 | 2 | 3 | 3 | 4 |
3 | 1 | 1 | 2 | 2 | 3 | 4 | 5 |
如果兑换0角,则只有一种组合,就是都不选(为了后边的计算)。
dp[i][j]={dp[i−1][j]dp[i−1][j]+dp[i][j−a[i]]j<a[i]j>=a[i]
注意: dp[i][j−a[i]] 而不是 dp[i−1][j−a[i]] ,这是完全背包和01背包的差别。因为每个都可以重复加入。
代码如下:
#include<iostream>
#include<stdio.h>
#include<memory.h>
using namespace std;
int func(int total)
{
int a[] = {0,1,2,5};
int dp[4][total+1];
memset(dp,0,sizeof(dp));
for(int i = 0; i <= 3; i++) dp[i][0] = 1;
for(int i = 1; i <= 3; i++)
{
for(int j = 0; j <= total; j++)
{
if(j >= a[i])
{
dp[i][j] = dp[i-1][j] + dp[i][j-a[i]];
}
else
{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[3][total];
}
空间也压缩后,代码如下:
int func2(int total)
{
int a[] = {0,1,2,5};
int dp[total+1] = {0};
dp[0] = 1;
for(int i = 1; i < 4; i++)
{
for(int j = 0; j <= total; j++)
{
if(j >= a[i])
{
dp[j] = dp[j] + dp[j-a[i]];
}
}
}
return dp[total];
}
题型2:给出coins数组和一个amount,求兑换所需的最少的硬币数。
题目和详细解答见:Coins