Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14246 Accepted Submission(s): 4952
php
这条题目里,咱们要先注意要达到最小余额,那么最大的菜价必定是最后要减的,那么咱们将这一组饭菜价格按从小到大排序,将最大的那个先放一边,咱们接下来就是要把剩下的一些菜价用咱们手头的余额减,固然必需要保证减去的金额小于等于sum-5,这样咱们才能在最后一次把最大的菜价刷掉。html
咱们作的转化就是,把除了最大菜价以外,其余的菜价装入一个sum-5 的背包里,看最大能装多少。ios
首先基于上一篇咱们的理论。(很重要!)数组
【理论讲解】http://www.cnblogs.com/fancy-itlife/p/4393213.html学习
首先看第一个条件—最优子结构。最大的装入量必定是若是装入第i个或者不装入第i个的两个选择之一。spa
第二个条件—子问题重叠。当完成一个阶段好比装第i个,我下面作的是对第i-1个进行抉择,你能够发现跟前面的问题同样,装仍是不装两个选择之一。这就是所谓的子问题重叠。设计
第三个条件—边界。这样的选择总归要有个结束的时候,当他到了第一个菜价时,若是它的背包容量也就是余额大于菜价,必定要装进去啊,这才会有可能变得比较大。若是不够的话,那必定是0。至此选择所有结束,而后是递归地返回上一层,直至抉择出正确答案。code
第四个条件—子问题独立。装仍是不装两个选择,双方的选择不会影响对方。htm
下面咱们就要来考虑一下,第五个条件—备忘录,也就是记忆化搜索,若是这个结果的值已经获得,那么咱们把它记录下来,以便后面再出现该值时直接使用。那么对于此问题的独立的小问题的就是执行了前n个菜价的抉择(装或不装),余额还剩m时的最大容量。能够用一个二维数组表示n*m。blog
那么上面已经详细叙述了该问题的求解方式,用记忆化的方式先来实现一下!
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define MAXN 1005 6 using namespace std; 7 int price[MAXN]; 8 int total[MAXN][MAXN]; 9 int dfs(int m,int k)//利用记忆化搜索实现01背包 10 { 11 int s; 12 if(total[m][k]>=0)//若是该值已经被记录了那么直接返回 13 return total[m][k]; 14 if(k==1)//处理边界值 15 { 16 if(m>=price[1])//若是剩余的额度大于等于该菜价,那么必定返回要将该菜价赋给s 17 s=price[1]; 18 else//若是剩余的额度小于该菜价,那么必定返回0 19 s=0; 20 } 21 else if(m>=price[k])//若是此时的额度是大于等于当前的菜价,则是这两种选择之中的一个 22 s=max((dfs(m-price[k],k-1)+price[k]),dfs(m,k-1)); 23 else//若是此时的额度是小于当前的菜价,则仅考虑不买这个菜的状况! 24 s=dfs(m,k-1); 25 total[m][k]=s;//记忆化 26 return s; 27 } 28 int main() 29 { 30 int n,i,sum,s; 31 while(scanf("%d",&n)!=EOF) 32 { 33 if(n==0) 34 break; 35 memset(total,-1,sizeof(total)); 36 for(i=1;i<=n;i++) 37 scanf("%d",&price[i]); 38 scanf("%d",&sum); 39 if(sum<=4) 40 printf("%d\n",sum); 41 else 42 { 43 sort(price+1,price+n+1); 44 s=sum; 45 sum=dfs(sum-5,n-1); 46 sum=s-sum-price[n]; 47 printf("%d\n",sum); 48 } 49 } 50 return 0; 51 }
但其实记忆化搜索的方式,比较适合初学时理解,可是其实它的不足在于递归开销太大,效率不算很高。
接下来咱们试着用递推的方式来实现该过程其实咱们彻底能够将每个子问题由小到大不断由前面的已解决的问题中推出,好比只有一个菜价时,根据余额和菜价的关系直接就能够获得最大的价值,(这也必定是正确且最大的)到达第二个菜价时,咱们抉择的仍是装仍是不装,装的话,咱们要把余额减去第二个菜价看看还剩的钱在前一个选择面前咱们能得到的最大金额再加上第二个菜价与不装第二个菜的最大金额比较大小,那么不装第二个菜,那就是第一个菜在这种余额下的最大金额。那么因为第一个阶段是知足最优的,那么你经过两种选择,也就获得了第二个阶段的最有状况。那么往复这样的状况咱们就得到了递推式的01背包求解。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define MAXN 1005 6 using namespace std; 7 int price[MAXN]; 8 int total[MAXN][MAXN]; 9 int main() 10 { 11 int n,m,i,j,s,sum; 12 while(scanf("%d",&n)!=EOF) 13 { 14 if(n==0) 15 break; 16 memset(total,0,sizeof(total)); 17 for(i=1;i<=n;i++) 18 scanf("%d",&price[i]); 19 scanf("%d",&sum); 20 if(sum<=4) 21 printf("%d\n",sum); 22 else 23 { 24 sort(price+1,price+n+1); 25 for(i=0;i<=sum-5;i++) 26 { 27 if(i<price[1]) 28 total[1][i]=0; 29 else 30 total[1][i]=price[1]; 31 } 32 for(i=2;i<=n-1;i++)//i表示依次选取前n个菜品(标号) 33 for(j=0;j<=sum-5;j++)//j表示余额 34 { 35 if(j<price[i]) 36 total[i][j]=total[i-1][j]; 37 else 38 total[i][j]=max(total[i-1][j-price[i]]+price[i],total[i-1][j]); 39 } 40 s=0; 41 for(i=1;i<=n-1;i++) 42 for(j=0;j<=sum-5;j++) 43 { 44 if(s<total[i][j]) 45 s=total[i][j]; 46 } 47 //cout<<s<<" "<<price[n]<<endl; 48 sum=sum-s-price[n]; 49 printf("%d\n",sum); 50 } 51 } 52 return 0; 53 }
那么咱们还能够再将空间减小为一维数组,缘由是什么呢,代码的注释里详细的解释了。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define MAXN 1005 6 using namespace std; 7 int price[MAXN]; 8 int total[MAXN]; 9 int main() 10 { 11 int n,m,i,j,s,sum; 12 while(scanf("%d",&n)!=EOF) 13 { 14 if(n==0) 15 break; 16 memset(total,0,sizeof(total)); 17 for(i=1;i<=n;i++) 18 scanf("%d",&price[i]); 19 scanf("%d",&sum); 20 if(sum<=4) 21 printf("%d\n",sum); 22 else 23 { 24 sort(price+1,price+n+1); 25 //为何只要用到一维数组,由于它的第二维只跟前一阶段有关, 26 //那么用一维数组就能够保存一个阶段的值,下一个阶段用上一个阶段来更新 27 for(i=1;i<=n-1;i++)//前n个阶段 28 for(j=sum-5;j>=0;j--)//表示此时该阶段若是为有j余额 29 { 30 if(j>=price[i]) 31 total[j]=max(total[j-price[i]]+price[i],total[j]); 32 /*为何须要逆序由于逆序能够带来的正确性是不言而喻的 33 我须要将前一阶段的j-price[i]余额的最大的消费获取到, 34 若是正向的话,我在求取一些余额较大的值时可能得到了该阶段 35 的j-price[i]的最大的消费额,由于小的余额是先更新的。 36 */ 37 } 38 s=0; 39 for(j=1;j<=sum-5;j++) 40 { 41 if(s<total[j]) 42 s=total[j]; 43 } 44 sum=sum-s-price[n]; 45 printf("%d\n",sum); 46 } 47 } 48 return 0; 49 }
再看看这三题的时间空间效率对比
本身也是才接触这类动态规划问题,也但愿此篇博文对你们学习01背包有所帮助!