dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数

Posted 指尖泛出的繁华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数相关的知识,希望对你有一定的参考价值。

一步一步循序渐进。

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)

详解和用树的思想分析符合dp

 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]

 

三道题目练手:

UVA,674

          

 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 }

 

 

UVA,147

 

注意点:存在小数,又因为题目提示都是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 }

 

 

UVA, 357

用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 }

 

 

----------------------------------

动态规划具体详解:动态规划,从新手到专家

背包问题:背包问题九

uva,357

题意是买东西问题,生活中最常见的问题竟然弄这么道题目,真是开眼界了。

给你一组货币 {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算法求花硬币的种类数的主要内容,如果未能解决你的问题,请参考以下文章

01完全多重背包模板

背包DP

第五讲 动态规划

01背包+完全背包+多重背包+单调队列

HDU 2844 Coins (多重背包问题DP)

01背包完全背包多重背包分组背包总结