找钱(动态规划)
找钱(动态规划)
说明
第一次写博客,如若不好,还请见谅。如有错误,欢迎指正。
题目描述
给定硬币的面额(值)1 = v1 < v2<…< vn,我们希望用尽可能少的硬币来兑换一定数量的零钱。假设vi和A是整数。因为v1= 1,所以总有解。形式上,这个问题的算法应该取一个数组V作为输入,其中V[i]是第i个面值的硬币的值,A的值为我们要去凑的值。该算法应该返回一个数组C,其中C[i]是有价值的硬币的数量V[i]作为变化返回和m的最小数量的硬币。判定能否组成A,如果能求所需每种硬币的数量。
分析
v[i]表示第i种硬币的面值,N表示硬币种数,A表示需要凑的价值(用"元"表示),c[i]表示第i中硬币需要的数量。
dp[i][j]表示前i种硬币,组成j元所需的最少硬币数,设第i种硬币用k枚,则第i种硬币就组成了k*v[i]元,那么前i-1枚硬币就必须组成j-k*v[i](j>=k* v[i]),如果dp[i-1][ j-k*v[i] ]+k<dp[i][j],那
么更新dp[i][j],直到dp[N][A]就是最小数目。
下面看看如何求每种硬币的数目(这部分我也说不太明白):
temp代表前i种硬币需要凑的面值,初始化temp=A,因为我们最终需要凑出A元。
我们逆序推导,如果最后一种硬币用了K个,那么前i-1种硬币就需要凑到temp-k*v[i]的面值,只需要更新temp=temp-k*v[i],i=i-1,继续推导就可以得出结论了。
那么我们怎么求第N枚硬币的数量呢?
硬币数量需要满足:
只要前N-1枚硬币组成j元时的最少硬币数+k枚第N枚硬币等于前N种硬币组成A元所需的最少硬币数就可以了。即dp[i][temp]==dp[i-1][temp-k*v[i]]+k。 但是面值是否恰好为temp呢?
只需要j+k*v[N-1]==temp,此处是求第N枚硬币的数量,接下来递推求出其他的就可以了。
如果没有第i枚就是第一枚,没有第i-1枚,做一下特判就可以了。
下面看代码吧
// 先输入硬币数和要凑的钱数
// 然后输入N种硬币的金额
#include <bits/stdc++.h>
using namespace std;
int N,A;
int MOD=1e9;
int main() {
//N种硬币,总和A
scanf("%d%d",&N,&A);
int dp[N+1][A+1],v[N+1],c[N+1];
memset(c,0,sizeof c);
for(int i=0;i<=N;i++){
for(int j=0;j<=A;j++)
dp[i][j]=MOD;
}
for(int i=1;i<=N;i++){
scanf("%d",&v[i]);
}
for(int i = 1; i <= N; i++){
for(int j = 1; j<= A; j++){
for(int k = 1;k*v[i]<=j ; k++){
int num=k*v[i];
if (num==j&&dp[i][j]>k)dp[i][j]=k;
else if (dp[i-1][j-num]!=MOD &&dp[i-1][j-num]+k<dp[i][j]){
dp[i][j]=dp[i-1][j-num]+k;
}
}
}
}
int temp=A;
for (int i=N;i>0;i--){
for(int j=temp;j>=0;j--){
int cha=temp-j;
if (j==0){
if (temp%v[i]==0&&temp/v[i]==dp[i][temp]){
c[i]=temp/v[i];
temp=0;
}
}
if (cha%v[i]==0&&dp[i-1][j]+cha/v[i]==dp[i][temp]){
c[i]=cha/v[i];
temp-=cha;
break;
}
}
}
printf("各种硬币数:\n");
for (int i=1;i<=N;i++){
printf("%d ",c[i]);
}
return 0;
}

浙公网安备 33010602011771号