硬币问题(字典最小序)-DAG动态规划问题
来源:互联网 发布:ppt录制旁白软件 编辑:程序博客网 时间:2024/06/10 10:51
题目:
有n种硬币,面值分别为V1,V2,...Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值!
分析:我们把每种面值看作一个点!表示“还需要凑足的面值”,初始状态为S,目标状态为0。那么若当前状态在i,每使用一个硬币j,状态便转移到i-Vj
因为求最大值跟求最小值类似,这里只贴记忆化搜索求最小值代码
代码: 下面dp1跟dp2功能相同。
不同dp2好理解一些。通过设置数组vis,标识是否访问过。牺牲空间换取效率,易读性。
<span style="font-size:24px;">#include"stdio.h"#include"stdlib.h"#include"algorithm"#include"string.h"#include"math.h"using namespace std;const int INF=1e9; const int max_coin=105;const int max_S=10005;int coin[max_coin];int d[max_S]; int vis[max_S];int n;//d(i) 从节点i出发到节点0的最长路径 int dp1(int S){//if(S==0) return 0;int &ans=d[S];if(ans!=-1) return ans; //记忆化搜索ans=INF;for(int i=0;i<n;i++) {if(S>=coin[i]) ans=min(ans,dp1(S-coin[i])+1);}ans=ans==INF?0:ans;return ans;}int dp2(int S) //牺牲空间换效率,容易理解 {//vis全部初始化为0 标记是否遍历过 if(vis[S]) return d[S]; vis[S]=1; int &ans=d[S]; ans=INF; for(int i=0;i<n;i++) if(S>=coin[i]) ans=min(ans,dp2(S-coin[i])+1); ans=ans==INF?0:ans;return ans;}int main(){int S;scanf("%d%d",&n,&S);for(int i=0;i<n;i++) scanf("%d",&coin[i]);memset(d,-1,sizeof(d));memset(vis,0,sizeof(vis)); int ans=dp1(S); printf("%d\n",ans);return 0; } </span>
如果使用递推求解呢?? 那么问题非常类似完全背包。。简单很多
上面只求出问题的解。
那么扩展一下,如果有相同的解,组合不一样,这时要求最小字典序列?该怎么求?
举个例子理解一下。 S=12,硬币 2 4 5 那么求最多硬币组合数。答案是:3.。 有两种可能 4 4 4 和 2 5 5 那么最小字典序列式是后者。
<span style="font-size:24px;">#include"stdio.h"#include"stdlib.h"#include"algorithm"#include"string.h"#include"math.h"using namespace std;const int INF=1e9; const int max_coin=105;const int max_S=10005;int coin[max_coin];int maxx[max_S],minn[max_S];//maxx[i] 货币为总额i时,可以有最多种货币组成。 int n,S;void Solve(){//这里没有处理 找不着的情况,很简单。 maxx[0]=minn[0]=0; for(int i=1;i<=S;i++) { maxx[i]=-INF; minn[i]=INF;}for(int i=0;i<n;i++) for(int j=coin[i];j<=S;j++) { maxx[j]=max(maxx[j],maxx[j-coin[i]]+1); minn[j]=min(minn[j],minn[j-coin[i]]+1); } // for(int i=0;i<=S;i++) // printf("%d %d\n",maxx[i],minn[i]);}void Print_path(int *d,int S){for(int i=0;i<n;i++) if(S>=coin[i]&&d[S]==d[S-coin[i]]+1) { printf("%d ",i); Print_path(d,S-coin[i]); break; }} int main(){scanf("%d%d",&n,&S);for(int i=0;i<n;i++) scanf("%d",&coin[i]);Solve();printf("%d\t%d\n",maxx[S],minn[S]);Print_path(minn,S);return 0;}</span>
很多玩家喜欢这样输出打印(也是牺牲空间换取时间,易读性)
<span style="font-size:24px;">#include"stdio.h"#include"stdlib.h"#include"algorithm"#include"string.h"#include"math.h"using namespace std;const int INF=1e9; const int max_coin=105;const int max_S=10005;int coin[max_coin];int max_path[max_S]; int min_path[max_S];//max_path[S] 记录满足minn[S]=minn[S-coin[i]]+1的最小i; int maxx[max_S],minn[max_S];int n,S;void Solve(){maxx[0]=minn[0]=0; for(int i=1;i<=S;i++) { maxx[i]=-INF; minn[i]=INF;}memset(max_path,0,sizeof(max_path));memset(min_path,0,sizeof(min_path));for(int i=1;i<=n;i++) for(int j=coin[i];j<=S;j++) { if(maxx[j]<maxx[j-coin[i]]+1) { maxx[j]=maxx[j-coin[i]]+1; max_path[j]=i; } if(minn[j]>minn[j-coin[i]]+1) { minn[j]=minn[j-coin[i]]+1; min_path[j]=i; } }}void Print_ans(int *d,int S){//路径逆序 while(S) { printf("%d ",d[S]); S-=coin[d[S]];}} int main(){scanf("%d%d",&n,&S);for(int i=1;i<=n;i++) scanf("%d",&coin[i]);Solve();printf("%d\t%d\n",maxx[S],minn[S]);Print_ans(min_path,S);return 0;}</span>
这里补充一个问题:
N元钱换为零钱,有多少不同的换法?币值包括1 2 5分,1 2 5角,1 2 5 10 20 50 100元。
例如:5分钱换为零钱,有以下4种换法:
1、5个1分
2、1个2分3个1分
3、2个2分1个1分
4、1个5分
(由于结果可能会很大,输出Mod 10^9 + 7的结果)
<span style="font-size:24px;">#include"stdio.h"#include"stdlib.h"#include"algorithm"#include"string.h"#include"math.h"int coin[14]={1,2,5,10,20,50,100,200,500,1000,2000,5000,10000};const int maxn=1e5+5;const int mod=1e9+7;int dp[maxn];//dp[i][j]表示前i种货币能够凑齐j钱组合数 int main(){ int n; scanf("%d",&n); dp[0]=1; for(int i=0;i<13;i++) for(int j=coin[i];j<=n;j++) dp[j]=(dp[j]%mod+dp[j-coin[i]]%mod)%mod; printf("%d\n",dp[n]); return 0; } </span>
1 0
- 硬币问题(字典最小序)-DAG动态规划问题
- 动态规划-DAG-硬币问题
- DAG 动态规划 -- 硬币问题
- 矩形嵌套(最小字典序)—DAG动态规划问题
- DAG上的动态规划------硬币问题
- 动态规划 DAG模型 硬币问题
- DAG上的动态规划------硬币问题
- DAG上的动态规划硬币问题
- DAG上的动态规划--硬币问题
- 硬币问题(DAG上的动态规划)
- ACM:DAG上的动态规划------硬币问题
- 动态规划----硬币问题
- 动态规划-硬币问题
- 动态规划-硬币问题
- 动态规划-硬币问题
- DAG之硬币问题DP(最长路及其字典序)
- DAG模型硬币问题
- DAG-硬币问题
- Java并发编程:深入剖析ThreadLocal
- 使用Handler子线程向主线程通信方式及源码解析
- 矩形嵌套(最小字典序)—DAG动态规划问题
- 如何获取EDU邮箱
- MBProgressHUD 介绍及使用
- 硬币问题(字典最小序)-DAG动态规划问题
- 【Ubuntu】单无线网卡创建无线热点共享网络连接
- QT 和 MFC
- Win32中用VS生成属于自己的动态链接库(DLL)并应用
- 数字音频备忘录
- 八一八那些男票脑洞大开时送的奇葩礼物
- leetcode:152. Maximum Product Subarray
- GeoHadoop 之 Hilbert 空间填充曲线 Java 实现(一)
- 【翻(xue)译(xi)】3D Game Programming With DirectX11 - 4.1