首页 > 试题广场 >

换零钱

[编程题]换零钱
  • 热度指数:2425 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
考虑仅用1分、5分、10分、25分和50分这5种硬币支付某一个给定的金额。例如需要支付11分钱,有一个1分和一个10分、一个1分和两个5分、六个1分和一个5分、十一个1分这4种方式。
请写一个程序,计算一个给定的金额有几种支付方式。
注:假定支付0元有1种方式。

输入描述:
输入包含多组数据。

每组数据包含一个正整数n(1≤n≤10000),即需要支付的金额。


输出描述:
对应每一组数据,输出一个正整数,表示替换方式的种数。
示例1

输入

11
26

输出

4
13
import java.util.Scanner;

/**
 * Created by Olive on 2017/8/6.
 */
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            int n = in.nextInt();
            long countWay = changeWay(n);
            System.out.println(countWay);
        }
    }

    // 硬币组合也是一种背包问题
    public static long changeWay(int n){
        if(n <= 0)
            return 1;
        int[] money = {1, 5, 10, 25, 50};
        // dp[i]表示,i分钱可以有多少种表示方式
        long[] dp = new long[n + 1];
        dp[0] = 1;
        // 然后遍历所有的零钱情况
        for(int i = 0; i < 5; i++){
            for(int j = money[i]; j <= n; j++){
                dp[j] = dp[j] + dp[j - money[i]];
            }
        }
        return dp[n];
    }
}


发表于 2017-08-06 15:27:48 回复(0)
更多回答
经典的动态规划问题。给定零钱change = {1, 5, 10, 25, 50},求target的兑换方法数。
其实这类问题非常经典且常用,例如自动贩卖机的自动找零程序,当然为了不找给顾客一
堆钢镚儿招致他的不满,需要零钱最少的方案数。此处单求找零钱的总方案数:
定义dp[i][j]的含义为:使用change[0] ~ change[i]兑换目标j的方法数,依次按行更新
完dp数组之后,最右下角的那个数就是使用给定的所有零钱兑换target的方案总数啦^-^!
按照动态规划一般的“三步走”策略(是我自己总结的三步走...):
 
第一步:计算dp数组的第一行,第一行dp[0][ j ]的含义就是仅仅使用change[0]兑换j的
方案总数,这个就简单了, j能被change[0]整除,就代表有一种方案,反之就是没有方案。
 
第二步:计算dp数组的第一列,第一列dp[i][0]的含义就是,使用change[0 ~ i]兑换0元的
方法数,这个就更简单了,兑换0元那就是不换呗,方案数自然就是1。
 
第三步:也是最复杂的一步,需要定制dp的更新策略了,对于这个问题,dp[i][j]如何更新
呢?根据dp[i][j]所表示的含义,即兑换j的总方案数,我们有如下等式成立(其实就是组合问题):
dp[i][j] (使用change[0 ~ i]兑换j)
= dp[i-1][j] (使用change[0 ~ i-1]兑换j,其实也就是使用0个change[i])
+ dp[i-1][j-change[i]]
        (使用change[0~i-1]兑换j-changes[i],其实也就是使用1个change[i])
+ dp[i-1][j-2*change[i]]
        (使用change[0~i-1]兑换j-2*changes[i],其实也就是使用2个change[i])
+...
+ dp[i-1][j-k*change[i]]
        (使用change[0~i-1]兑换j-k*changes[i],其实也就是使用k个change[i])
当然这里k有个约束:0 < k 且 k*changes[i] <= j,对吧,不能比目标j还大吧。
 
我们把所有的方案累加起来就是兑换j的总方案数dp[i][j],按照这个思路写的代码如下 
long long solution(vector<int> changes, int target){
    int n = changes.size();
    vector<vector<long long>> dp(n, vector<long long>(target + 1));
    for (int i = 0; i <= target; i++){
        if (i % changes[0] == 0)
            dp[0][i] = 1;
    }
    for (int i = 0; i < n; i++){
        dp[i][0] = 1;
    }
    for (int i = 1; i < n; i++){
        for (int j = 1; j <= target; j++){
            int num = 0;
            for (int k = 0; k*changes[i] <= j; k++)
                num += dp[i - 1][j - k * changes[i]];
            dp[i][j] = num;
        }
    }
    return dp[n - 1][target];
}
 
思路没有问题,但是时间复杂度有点高喂,仔细分析一下上述思路中dp[i][j]的构成,
分为k + 1个分式,我们把第一个式子看成一组,后面k个式子看成一组,那么第一组
式子表示的含义就是不使用change[i]的时候构成 j 的方案数;第二组式子的含义就是
使用了change[0 ~ i-1] + change[i]的时候构成 j 的方案数,并且这k个式子对使用1~k
个change[i]的情况进行了枚举!!那不就是使用了change[0 ~ i]吗?没错,那构成的钱
数是多少呢?是dp[i - 1][j - change[i]],...,dp[i - 1][j - k * change[i]],到
底是哪个呢?按最多的那个钱数算,没错的!所以第二组式子所表示的含义就是使用了
change[0 ~ i]构成dp[i - 1][j - change[i]]的总方案数!!!所以就有dp的更新公式
为:dp[i][j] = dp[i - 1][j] + dp[i][j - change[i]],按照这个思路给出的求解方案
是: 
long long solution(vector<int> changes, int target){
    int n = changes.size();
    vector<vector<long long>> dp(n, vector<long long>(target + 1));
    for (int i = 0; i <= target; i++){
        if (i % changes[0] == 0)
            dp[0][i] = 1;
    }
    for (int i = 0; i < n; i++){
        dp[i][0] = 1;
    }
    for (int i = 1; i < n; i++){
        for (int j = 1; j <= target; j++){
            if (j < changes[i])
                dp[i][j] = dp[i - 1][j];
            else
                dp[i][j] = dp[i - 1][j] + dp[i][j - changes[i]];
        }
    }   
    return dp[n - 1][target];
}

编辑于 2018-01-05 19:49:32 回复(2)
//动态规划,思路在讨论区大同小异,在此仅记录一下自己的代码,仅参考!
#include<iostream>
#include<vector>
 
#define N 5
using namespace std;
int main(void)
{
    int m[N + 1] = { 0,1,5,10,25,50 };
    int money;
    while (cin >> money)
    {
        long long **dp = new long long *[N + 1];
        for (int i = 0; i <= N; i++)
            dp[i] = new long long[money + 1];
        //边界条件
        dp[0][0] = 1;
        for (int i = 1; i <= N; i++)
            dp[i][0] = 1;//第一列全部为1
        for (int i = 1; i <= money; i++)
            dp[0][i] = 0;//第一行全部为0
 
        for (int i = 1; i <= N; i++)
        {
            for (int j = 1; j <= money; j++)
            {
                if (j >= m[i])
                    dp[i][j] = dp[i - 1][j] + dp[i][j - m[i]];
                else
                    dp[i][j] = dp[i - 1][j];
            }
        }
        cout << dp[N][money] << endl;
        for (int i = 0; i < N + 1; i++)
            delete dp[i];
        delete[] dp;
    }
         
    return 0;
}

编辑于 2019-08-08 10:16:34 回复(0)
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
#include <map>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <exception>
#include <iomanip>
#include <memory>
#include <sstream>

using namespace std;

int main(int argc, char** argv)
{
	//freopen("in.txt", "r", stdin);
	vector<int> input;
	int n, maxVal = 0;
	while (cin >> n)
	{
		input.emplace_back(n);
		maxVal = max(maxVal, n);
	}

	vector<long long> dp(maxVal + 1, 0), coins{ 1, 5, 10, 25, 50 };
	dp[0] = 1;
	for (auto& coin:coins)
		for (int j = coin; j <= maxVal; ++j)
			dp[j] += dp[j - coin];

	for (auto& i : input) cout << dp[i] << endl;

	return 0;
}

发表于 2017-07-11 16:15:35 回复(0)
#include <iostream>
#include <vector>
using namespace std;
long countWays(vector<int> changes, int n, int x) {
	vector<long> vec(x+1,0);
    for(int i=0;i<n;i++){
    	if(changes[i]<=x){
        	vec[changes[i]]++;
        	for(int j=1;j+changes[i]<=x;j++)
            	vec[j+changes[i]]+=vec[j];
        }
    }
    return vec[x];
}
int main(){
    int N;
    int p[]={1,5,10,25,50};
    vector<int> vec(p,p+5);
    while(cin>>N){
        cout<<countWays(vec,5,N)<<endl;
    }
    return 0;
}

发表于 2016-09-05 11:44:14 回复(0)
def ling(n):#零钱构成 a=[] for i in range(n+1): for j in range(int(n/5+1)): for k in range(int(n/10+1)): for l in range(int(n/25+1)): for m in range(int(n/50+1)): if n==i+5*j+10*k+25*l+50*m: a.append(str([i,j,k,l,m])) return a while 1 : n=int(input('n')) a=ling(n) print('\n'.join(x for x in a))
发表于 2019-04-08 21:51:59 回复(0)
// write your code here
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()) {
            int m=sc.nextInt();
            int[] v= {1,5,10,25,50};
            long[] dp=new long[m+1];
            dp[0]=1;
            for(int i=0;i<v.length;i++) {
                for(int j=1;j<=m;j++) {
                    if(j>=v[i]) {
                        dp[j]+=dp[j-v[i]];
                    }
                }
            }
            System.out.println(dp[m]);
        }
    }
}

发表于 2018-01-26 15:25:37 回复(0)
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int[] money = {1, 5, 10, 25, 50};
		while (sc.hasNext()) {
			int n = sc.nextInt();
			long[] dp = new long[n + 1];
			dp[0] = 1;
			for (int i = 0; i < money.length; i ++ ) {
				for (int j = money[i]; j < dp.length; j ++ ) {
					dp[j] += dp[j - money[i]];
				}
			}
			System.out.println(dp[n]);
		}
	}
}

发表于 2016-10-14 00:25:15 回复(0)
import java.util.*;
public class Main{
    private static int[] change = {1,5,10,25,50};
    public static void main(String[] args){       
        Scanner in = new Scanner(System.in);
        while(in.hasNext()){
			int money = in.nextInt();
            System.out.print(Exchange(money,change.length));
        }
    }
    
    private static int Exchange(int money,int n){
        if(money==1){
            return 1;
        }else if(money<0||n==0){
            return 0;
        }else{
            return Exchange(money,n-1) + Exchange(money-change[n-1],n);
        }
    }
}

发表于 2016-09-18 20:49:27 回复(1)
#include<iostream>
using namespace std;
int money[6] ={0,1,5,10,25,50};
long long dp[6][10001]={0};//此处要为long long不然会超出范围
void GetDP()
{
    for(int i = 0;i < 6;++i)
{
   dp[i][0] = 1;
}
for(int i = 1;i < 6;++i)
{
   for(int j = 1;j < 10001;++j)
{
   if(j - money[i] >= 0)
dp[i][j] = dp[i-1][j] + dp[i][j-money[i]];
else
dp[i][j] = dp[i-1][j];
}
}
}
int main()
{
     GetDP();
int n;
while(cin>>n)
{
    cout<<dp[5][n]<<endl;
}
}
发表于 2016-08-21 15:56:32 回复(0)
可使用母函数法
#include<iostream>
#include<string.h>
#define MAXNUM 10001      //最高次数
unsigned long a[MAXNUM];  
unsigned long b[MAXNUM];
unsigned long c[MAXNUM];  //保存结果

void Poly()       //多项式相乘
{
	int i;
	int j;
	memset(c, 0, sizeof(c));   //将数组c全部初始化为0;
	for (i = 0; i < MAXNUM; i++)
	{
		for (j = 0; j < MAXNUM - i; j++)    //j < MAXNUM - i,确保i+j不越界
		{
			c[i + j] += a[i] * b[j];    
		}
	}
}

int main()
{
	for (int i = 0; i < MAXNUM; i++)
	{
		a[i] = 1;
	}
	int num[5] = { 1,5,10,25,50 };
	for (int i = 1; i < 5; i++)
	{
        memset(b,0,sizeof(b));
		for (int j = 0; j < MAXNUM; j += num[i])
		{
			b[j] = 1;
		}
		Poly();
		memcpy(a, c, sizeof(c));
	}
	int input;
	while (std::cin >> input)
		std::cout << a[input] << std::endl;
	//system("pause");
	return 0;
}

发表于 2016-08-16 14:30:49 回复(0)
#include <iostream>

const int N = 10000;

long dp[N + 1] = {0};
int MON[5] = {1,5,10,25,50};

int main()
{
  dp[0] = 1;
  for (int i = 0; i < 5; ++i) {
    for (int j = MON[i]; j < N + 1; ++j ) {
            dp[j] += dp[j - MON[i]];
        }
    }

  int n;
  while (std::cin>>n) {
      std::cout << dp[n] << "\n";
  }
}
多重背包问题的一维数组解法,增加两个技巧:
1. 一次性把所有可能的问题都算出来,不必每个输入都单独计算。
2.价值计算从最小的币值开始(或者反向计算到币值),即子循环条件改为
for (int v = N; v >= MON[i]; --v)

发表于 2016-08-05 17:20:46 回复(0)
// write your code here cpp
#include <iostream>
using namespace std;
//typedef int MM[5];
int main()
{
 int N;
 int MON[5]{1,5,10,25,50};
 while(cin>>N)
 {
 unsigned long long **dp = new unsigned long long *[2];
 dp[0] = new unsigned long long [N+1];
 dp[1] = new unsigned long long [N+1];
 for(int i=0;i<N+1;i++)
 {
 dp[0][i] = (i>=MON[0])?MON[0]:0;
 }
 dp[0][0] = 1;
 dp[1][0] = 1;
 for(int i = 1;i<5;i++)
 {
 for(int j=1;j<N+1;j++)
 {
 if( j >= MON[i])
 {
 dp[1][j] = dp[0][j] + dp[1][j-MON[i]];
 }
 else
 {
 dp[1][j] = dp[0][j];
 }
 }
 for(int kk=0;kk<N+1;kk++)
 {
 dp[0][kk] = dp[1][kk];
 }
 }
 cout<<dp[1][N]<<endl;
 delete []dp[0];
 delete []dp[1];
 delete []dp;
 }



 return 0;
}


发表于 2016-08-04 09:27:35 回复(0)
/** 答案错误:您提交的程序没有通过所有的测试用例

    测试用例:
    9001

    对应输出应该为:

    4466984251

    你的输出为:

172016955 严重怀疑测试用例答案有错误,用了各种方法,答案都是一样的
ps:如果哪位大神通过了,请告知:9001的结果到底是什么 >_< 

不管怎样,用的动态规划的思想,这道题是一个整数划分的变形,一般整数划分的加数都是连续的数,从1~n都可以是划分后的加数,这里限定了划分后的加数只能是1,5,10,25,50这5个数。
A.没有限定加数情况的状态转移方程:有两种情况:
    1.n>=m: dp[n][m] = dp[n][m-1] + dp[n-m][m],
    2.n<m: dp[n][m] = dp[n][n],
    其中dp[n][m]表示整数n在加数不大于m的情况下的划分个数。
B.在限定加数的情况下:(以此题为例,加数按顺序放入数组v中)
    初始:dp[k][v[0]] = 1; 0<=k<=maxn;
    1.n>=v[i]: dp[n][v[i]] = dp[n][v[i-1]] + dp[n-v[i]][v[i]]; 1<=i<=4
    2.n<v[i]: dp[n][v[i]] = dp[n][v[i-1]]
**/
import java.util.Scanner;

public class Main {

	public static final int maxn = 10000;
	
	public static final int[] v = {1,5,10,25,50};
	
	public static int[][] f = new int[maxn+1][51];
	
	public static void dp(){
		for(int i=0;i<=maxn;i++){
			f[i][v[0]] = 1;
		}
		for(int i=0;i<=maxn;i++){
			for(int j=1;j<5;j++){
				if(v[j]<=i)
					f[i][v[j]] = f[i][v[j-1]] + f[i-v[j]][v[j]];
				else
					f[i][v[j]] = f[i][v[j-1]];
			}
		}
	}
	
	public static void main(String[] args) {
		dp();
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()){
			int n = sc.nextInt();
			System.out.println(f[n][v[4]]);
		}
	}
}

发表于 2016-02-21 17:13:02 回复(2)
/*
题目分析:
可以理解为有限定的整数拆分,也可以看作求解多重背包问题的方案数。
鉴于大多人用背包的方法求解,本文回到课本,采用组合数学里的母函数方法求解。

解题思路:
用a,b,c,d,e分别表示1分、5分、10分、25分和50分的币种
且每种钱币的数量不限,可用多个或者不用
则可得到母函数表达式(其中e=a^50,d=a^25,c=a^10,b=a^5)
(1+a+a^2+....)*(1+b+b^2+....)*(1+c+c^2+....)*(1+d+d^2+....)*(1+e+e^2+....)
最后根据题目所需要的结果
得到一个关于abcde的多项式。且每一项中abcde的指数和为某结果时对应的系数即为答案。

举例:
当对11分换零钱时
(1+a+a^2+....+a^11)*(1+b+b^2)*(1+c)
得到指数和为11的有如下
a^11 a^6*b a*c a*b^2 (其中c=a^10,b=a^5)
总共4个,所以求得结果为4
*/

//代码
#include <stdio.h>
#include <string.h>
#define MAX_SIZE 10000

int c1[MAX_SIZE]={0};
int c2[MAX_SIZE]={0};

int fun(int a[],int array_size,int n)
{
int i,j,k;
//初始化
memset(c1,0,MAX_SIZE);
memset(c2,0,MAX_SIZE);
for (i = 0;a[0]*i<=n;i+=a[0])
{
c1[i] = 1;
}

for (i = 1;i<array_size;i++)
{
for (j = 0;j<=n;j++)
{
for (k = 0;k+j<=n;k += a[i])
{
c2[j+k] += c1[j];
}
}
for (j = 0;j<=n;j++)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
return c1[n];
}

void main()
{
int a[5]={1,5,10,25,50};
int re;
//测试部分
re = fun(a,5,2);
printf("%d\n",re);
}
发表于 2015-01-28 16:11:12 回复(0)
#include<stdio.h>
#include<string.h>

const int  maxn=10000;
int d[maxn];
int v[5]={1,5,10,25,50};

void dp(int n)
{
    memset(d,0,maxn);
    d[0]=1;
    for(int j=0;j<5;j++)
    {
        for(int i=0;i<maxn;i++)
        {
            if(i+v[j]<=maxn)
                d[i+v[j]]+=d[i];
        }
    }
}

int main()
{
    int n;
    dp(maxn);
    while(~scanf("%d",&n))
    {
        printf("%d\n",d[n]);
    }
    return 0;
}

发表于 2015-01-19 17:58:11 回复(0)

int computeCount(int n)
{
int money[6] = {0, 1, 5, 10, 25, 50};
int result = 0;

int ** count = new int*[n + 1]; 
for(int i = 0; i < n + 1; i++)
{
count[i] = new int[6];
memset(count[i], 0, sizeof(int) * 6);
}

for(int i = 1; i <= n; i++)
count[i][1] = 1;

for(int i = 1; i <= 5; i++)
count[0][i] = 1;

for(int i = 1; i <= n; i++)
{
for(int j = 2; j <= 5; j++)
count[i][j] = count[i][j - 1] + ( i >= money[j] ? count[i - money[j]][j] : 0);
}

result = count[n][5];

for(int i = 0; i < n + 1; i++)
{
delete [] count[i];
}

return result;
}
发表于 2015-01-09 11:20:39 回复(0)