dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数
Posted 指尖泛出的繁华
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数相关的知识,希望对你有一定的参考价值。
一步一步循序渐进。
具体思想:给你 N元,然后你有几种零钱S={S1,S2...,Sm} (每种零钱数量不限).
问:凑成N有多少种组合方式 即N=x1 * S1+x2*S2+...+xk*Sk (xk>=0,k=1,2..m)
设有f(x)中组合方式
有两种解答(自底向上回溯): 1.不用第m种货币 f(N,m-1)
2.用第m种货币 f(N-Sm,m)
总的组合方式为f(N,m)=f(N,m-1)+f(N-Sm,m)
anything is nonsense,show me the code
1 def count( n, m ): 2 if n < 0 or m <= 0: #m < 0 for zero indexed programming languages 3 return 0 4 if n == 0: # needs be checked after n & m, as if n = 0 and m < 0 then it would return 1, which should not be the case. 5 return 1 6 7 return count( n, m - 1 ) + count( n - S[m], m )
----------------------------------
因为f(N,m)<f(x,y) <--> n<=x,m<=y (n,m)!=(x,y).因此满足dp的最优子结构条件
时间复杂度会有O(n*m)
1 func count( n, m ) 2 3 for i from 0 to n 4 for j from 0 to m 5 if i equals 0 6 table[i, j] = 1 7 else if j equals 0 8 table[i, j] = 1 if i%S[j] equals 0 else 0 9 else if S_j greater than i 10 table[i, j] = table[i, j - 1] 11 else 12 table[i, j] = table[i - S_j, j] + table[i, j-1] 13 14 return table[n, m]
三道题目练手:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 int S[5]={1,5,10,25,50}; 7 8 int count(int n) 9 { 10 int table[n+1]; 11 memset(table,0,sizeof(table)); 12 13 table[0]=1; 14 for(int i=0;i<5;i++) 15 for(int j=S[i];j<=n;j++) 16 table[j]+=table[j-S[i]]; 17 return table[n]; 18 } 19 20 int main() 21 { 22 int n; 23 while(~scanf("%d",&n)) 24 { 25 printf("%d\n",count(n)); 26 } 27 return 0; 28 }
注意点:存在小数,又因为题目提示都是5的倍数,所以每项乘以100作整数处理
.......这题我想砸电脑。。我5000/5=1250....wa 十几分钟。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <string.h> 5 using namespace std; 6 7 int S[11]={1,2,4,10,20,40,100,200,400,1000,2000}; 8 9 int main() 10 { 11 long long table[6010]={1}; 12 for(int i=0;i<11;i++) 13 for(int j=S[i];j<=6000;j++) 14 table[j]+=table[j-S[i]]; 15 double n; 16 while(scanf("%lf",&n)!=EOF) 17 { 18 int c=(int)(n*20+0.5); 19 if(n==0.00) 20 break; 21 printf("%6.2lf%17lld\n",n,table[c]); 22 } 23 return 0; 24 }
用long long表示组合的的种类比较多
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 int S[5]={1,5,10,25,50}; 7 const int maxn=30010; 8 //水题 9 int main() 10 { 11 int n; 12 long long table[maxn]; 13 memset(table,0,sizeof(table)); 14 table[0]=1; 15 for(int i=0;i<5;i++) 16 for(int j=S[i];j<=maxn;j++) 17 table[j]+=table[j-S[i]]; 18 while(~scanf("%d",&n)) 19 { 20 if(table[n]==1) 21 printf("There is only 1 way to produce %d cents change.\n",n); 22 else 23 printf("There are %lld ways to produce %d cents change.\n",table[n],n); 24 } 25 return 0; 26 }
----------------------------------
动态规划具体详解:动态规划,从新手到专家
背包问题:背包问题九讲
题意是买东西问题,生活中最常见的问题竟然弄这么道题目,真是开眼界了。
给你一组货币 {5c,10c,20c,50c,1,2}
输入:每种货币的个数,需要付的钱
条件:售货员有每种货币充足,你每种货币数量已知,要你求付钱所用最少的硬币数量(最优子结构)
模拟背包:需要付的钱为背包容量,每种货币为要装进背包的容量,
所有的硬币数=我付的硬币数+售货员找我的硬币数(最小)
用两部分的最小达到总体的最小(局部最优解求总体最优解)
满足以上两个要素为dp问题。
完全背包:售货员硬币足够。 用它算出售货员要找的硬币的最小个数
多重背包:我要付的硬币的最小个数
1 #include <cstdio> 2 #include <string.h> 3 #include <math.h> 4 5 using namespace std; 6 7 const int MAX=2000; 8 int coin[6]={1,2,4,10,20,40},f1[MAX+1],f2[MAX+1],n[6]; 9 10 void zero_one(int *f,int t) 11 { 12 for(int i=MAX;i>=t;i--) 13 if(f[i-t]!=-1 && (f[i]==-1 || f[i-t]+1<f[i])) 14 f[i]=f[i-t]+1; 15 } 16 17 void complete(int *f,int t) 18 { 19 for(int i=t;i<=MAX;i++) 20 if(f[i-t]!=-1 && (f[i]==-1 || f[i-t]+1<f[i])) 21 f[i]=f[i-t]+1; 22 } 23 24 void multiple(int *f) 25 { 26 for(int i=0;i<6;i++) 27 if(n[i]*coin[i]>MAX) 28 complete(f,coin[i]); 29 else 30 { 31 while(n[i]--) 32 zero_one(f,coin[i]); 33 } 34 } 35 36 int main() 37 { 38 float t; 39 int total; 40 while(true) 41 { 42 total=0; 43 for(int i=0;i<6;i++) 44 { 45 scanf("%d",&n[i]); 46 total+=n[i]; 47 } 48 if(total==0) 49 break; 50 scanf("%f",&t); 51 int tar=(int)(t*20+0.5f); 52 memset(f1,-1,sizeof(f1));f1[0]=0; 53 memset(f2,-1,sizeof(f2));f2[0]=0; 54 55 multiple(f1); 56 57 for(int i=0;i<6;i++) 58 complete(f2,coin[i]); 59 int ans=MAX; 60 61 for(int i=tar;i<=MAX;i++) 62 { 63 if(f1[i]!=-1 && f2[i-tar]!=-1 && ans>f1[i]+f2[i-tar]) 64 ans=f1[i]+f2[i-tar]; 65 } 66 printf("%3d\n",ans); 67 } 68 return 0; 69 }
具体各个背包问题详解可以参考:低调小一
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
以上是关于dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数的主要内容,如果未能解决你的问题,请参考以下文章