考虑仅用1分、5分、10分、25分和50分这5种硬币支付某一个给定的金额。例如需要支付11分钱,有一个1分和一个10分、一个1分和两个5分、六个1分和一个5分、十一个1分这4种方式。
请写一个程序,计算一个给定的金额有几种支付方式。
注:假定支付0元有1种方式。
输入包含多组数据。
每组数据包含一个正整数n(1≤n≤10000),即需要支付的金额。
对应每一组数据,输出一个正整数,表示替换方式的种数。
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]; } }
经典的动态规划问题。给定零钱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]; }
//动态规划,思路在讨论区大同小异,在此仅记录一下自己的代码,仅参考! #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; }
#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; }
#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; }
// 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]); } } }
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]); } } }
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); } } }
可使用母函数法 #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; }
#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"; } }
for (int v = N; v >= MON[i]; --v)
// 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;}
/** 答案错误:您提交的程序没有通过所有的测试用例
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]]);
}
}
}