满减优惠求具体组合问题(结果包括具体组合情况和最小消费值)

DP法求满减优惠组合问题

一、原问题描述

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

最近天气炎热,小Ho天天宅在家里叫外卖。他常吃的一家餐馆一共有N道菜品,价格分别是A1, A2, … AN元。并且如果消费总计满X元,还能享受优惠。小Ho是一个不薅羊毛不舒服斯基的人,他希望选择若干道不同的菜品,使得总价在不低于X元的同时尽量低。

你能算出这一餐小Ho最少消费多少元吗?
输入

第一行包含两个整数N和X,(1 <= N <= 20, 1 <= X <= 100)

第二行包含N个整数A1, A2, …, AN。(1 <= Ai <= 100)
输出

输出最少的消费。如果小Ho把N道菜都买了还不能达到X元的优惠标准,输出-1。
样例输入

10 50
9 9 9 9 9 9 9 9 9 8

样例输出

53

二、算法参考链接与分析
1、求最少消费的值算法
促销中“满X优惠”问题的两种解法:动态规划和枚举法
枚举法: 瓶颈在于 for(int s=1; s<(1<<n); s++)中,由于是2^n指数级复杂度,而int整型最大值2的32次方,即n的值理论上不超过32,即便是long型,n最大不超过64。
动态规划法:只能求解最少消费的值,但是没有求解具体相关组合情况

本文算法参考链接
缺陷:(1)算法存在bug,dp[j]=dp[j-a[i]]的前提是dp[j]==0,否则会出现dp[j]=1后,在后面的循环中可能被置0。即该组合情况存在,但后面又被否决。(2)算法求解最少消费的值,没有具体相关组合情况

2、求具体组合情况
基本上没有合适的方法专门求具体组合,接近的有以下几个:
硬币组合问题 ------ 只是组合的数量,而不是具体情况。
分硬币问题(贪心) ------ 求最少商品的数量,而不是最优的情况,即局部最优,非全局最优。
三、本文算法
1、数组a[]是商品价格,求解所有商品所有可能的和的情况,并保存在数组dp[sum+1]中,sum为所有商品总价之和,数组下标对应某一组合的和,值为0,即该下标的值不是任一组合的总和。数组b[]保存第一次出现这种结果时,该组合的最后一个商品。最终得到最少消费的值ans。

  for( i=0;i<n;i++){
        for( j=sum;j>=a[i];j--){
             if(dp[j]==0)  dp[j]=dp[j-a[i]];                               
  			  if(dp[j]==1&&b[j]==-1)  b[j]=i;                            
         }  }
         
       for(int k=X;k<=sum;k++){
         if(dp[k]==1){
         printf("%d\n",k);
         ans = k;
         break;
     } }

2、 利用数组dp[]保存了所有可能的组合情况的和,数组b[]保存了第一次出现相应组合的和,最后一个商品的位置。给定最少消费的值,逆推相应的组合,保存在数组c[]中。例如商品数量为10,最优组合用到第1,3,5,7,9个商品,则c[]={1,0,1,0,1,0,1,0,1,0}。

注意:组合不一定一种,本文只求出最先出现该结果的组合,不保证商品数量最少。

     for( i = b[ans], j = ans; i>=0; )
     {
             if(j>a[i]){
                     if(dp[j-a[i]]==0) { i--; continue;}
                             else if(dp[j-a[i]]==1)
                             { 
                                     c[i]=1;
                                     ans=ans-a[i];
                                     i=b[ans];
                                     continue;
                             }
                       }
     else if(j<a[i]&&j>0)  { i--; continue;}
             else if(j==a[i]) {
                                     c[i]=1;
                                      break;
                              }                                  
      }

3、算法优点
(1)与枚举法相比,瓶颈提高。瓶颈在于商品价值总和sum值最大为2的32次方,而不是数量最大为32。
(2)速度:求最少消费值的复杂度是商品数量乘以商品总价值,即n*sum;
求具体组合情况的复杂度是商品数量n。实际上求最少消费值的过程中已经潜在求出相应组合,而且求出商品数量往往远低于商品价值,求具体组合情况的时间消耗可以忽略。

猜你喜欢

转载自blog.csdn.net/qq_41551654/article/details/87914533