硬币找零
-
描述
-
在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计 算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资。我们应该注意到,人民币的硬币系统是 100,50,20,10,5,2,1,0.5,0.2,0.1,0.05,0.02,0.01 元,采用这些硬币我们可以对任何一个工资数用贪心算法求出其最少硬币数。但不幸的是: 我们可能没有这样一种好的硬币系统, 因此用贪心算法不能求出最少的硬币数, 甚至有些金钱总数还不能用这些硬币找零。例如,如果硬币系统是 40,30,25 元,那么 37 元就不能用这些硬币找零;95 元的最少找零硬币数是 3。又如,硬币系统是 10,7,5,1 元,那么 12 元用贪心法得到的硬币数为 3,而最少硬币数是 2。你的任务就是:对于任意的硬币系统和一个金钱数,请你编程求出最少的找零硬币数;如果不能用这些硬币找零,请给出一种找零方法,使剩下的钱最少。
解题思路:
一开始的时候我直接深搜了一下,其实就是想调戏下OJ君:
然后超时了,那就动规吧。#include<stdio.h> #include<stdlib.h> #define N 50 int a[N]; int ans; int cmp(const void *x,const void *y){ return *(int *)x>*(int *)y?1:-1; } int MinRest; int Min(int a,int b){ return a<b?a:b; } int n; void dfs(int cur,int rest,int Count){ if(rest<0) return ; if(rest<=MinRest){ if(rest<MinRest) ans=Count; else ans=Min(ans,Count); MinRest=rest; } for(int i=0;i<n;i++){ dfs(i,rest-a[i],Count+1); } } int main() { int sum; int i; while(scanf("%d%d",&n,&sum)&&(n||sum)){ for(i=0;i<n;i++) scanf("%d",&a[i]); qsort(a,n,sizeof(a[0]),cmp); MinRest=sum; ans=1<<31-1; dfs(0,sum,0); printf("%d\n",ans); } }
这个题有三个点:
1)所有的硬币都是无限的;
2)要用尽可能少的硬币;
3)无法找零要尽可能的靠近。
咦~~怎么有点像背包啊,给你n个物品,每个物品都一个价值和一个重量,背包有一个固定的容量。。。好吧YY一下而已啦!
构建dp[i]数组,i块钱最少需要多少个硬币。那么状态转移方程必然是(a[i]用来存那套硬币系统)dp[i]=min(dp[i-a[k]]k={0..n});
#include<stdio.h> #include<algorithm> #include<string.h> #define N 100008 unsigned int dp[N]; unsigned int a[51]; unsigned int n,sum; void Dp(){ unsigned int min,i,j; for(i=1;i<=sum;i++){ min=100008; for(j=0;a[j]<=i&&j<n;j++){ if(min>dp[i-a[j]]&&(dp[i-a[j]]!=0||i==a[j])) min=dp[i-a[j]]; } if(min!=100008) dp[i]=min+1; } } int main(){ unsigned int i; while(scanf("%d%d",&n,&sum)&&(sum||n)){ memset(dp,0,sizeof(dp)); for(i=0;i<n;i++){ scanf("%d",&a[i]); dp[a[i]]=1; } std::sort(a,a+n); Dp(); while(dp[sum]==0) sum--; printf("%d\n",dp[sum]); } }