数字序列加入+,*运算符后取得最大值问题; 动态规划;打破传统从决策入手思想;找出问题的特有性质;从例子入手找特点

假设有一个数组A,里面的元素都大于1的正整数,只采用加号或乘号,不改变数组元素的位置,如何使最后结果最大?比如:
A={2,1,1,1,1,2}那么就用加号结果为8,B={3,1,3}那么就用乘号结果为9。

 

一开始我想从决策入手,即对每个位置逐次加上+或者*,然后利用回溯方面的思想或者动态规划来做,可是发现一旦遇到*,或者连续*,问题显得很复杂,无法实施。

 

这个题的特点就是发现特有的性质。  从+断开的地方左右是没有联系的,所以我们应该来安排+号,实现问题的分割。

我们从序列的头开始先找第一个加号,然后计算+左边的乘积,然后计算+右边的最大值。 右边也是同样的问题。

 

从各种加号的位置分布情况中找到一个最有的值。

 

 

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. #define MIN -1  
  5.   
  6. class Sum  
  7. {  
  8. private:  
  9.     int *result;//动态规划表格,result[n]表示1....n做加乘综合运算后的最大值  
  10.     int *path;//在动态规划递推过程中记录加号出现位置  
  11.     int *arr;//存放num个数字  
  12.     int num;//数字序列的长度  
  13. public:  
  14.     //构造函数  
  15.     Sum(int num)//num个数字  
  16.     {  
  17.         this->num=num;  
  18.         result=new int[num+1];//用数组下标的1到n  
  19.         path=new int[num+1];//记录路径也只用1到n  
  20.         arr=new int[num+1];//开辟数组存放数字序列,只用1到n  
  21.     }  
  22.     //输入数字序列  
  23.     void input()  
  24.     {  
  25.         for(int i=1;i<=num;i++)  
  26.         {  
  27.             cin>>arr[i];  
  28.         }  
  29.     }  
  30.     //动态规划求解 ,公式: result[n]=max { (x[k]*x[k+1]*x[k+2]*....x[n])+result[k-1] }  ,k取1....n   
  31.     void maxResult()  
  32.     {  
  33.         //初始化reslut[0]=0,即0个数据的最大值为0,初始化result[1]=arr[1],即前1个数据的最大值为本身  
  34.         result[0]=0;  
  35.         result[1]=arr[1];  
  36.         path[1]=1;  
  37.   
  38.         //递推求解,最终result[n]表示前n个数的最大值  
  39.         int localSum;  
  40.         int temp;  
  41.   
  42.         for(int i=2;i<=num;i++)  
  43.         {  
  44.             result[i]=MIN;  
  45.             localSum=1;  
  46.   
  47.             for(int k=i;k>=1;k--)  
  48.             {  
  49.                 //求x[k]*x[k+1]*.....x[i]  
  50.                 localSum*=arr[k];  
  51.                 //求x[k]*x[k+1]*.....x[i]+result[k-1]  
  52.                 temp=localSum+result[k-1];  
  53.                 //记录k取不同值时所能达到的最大值,并记录断点path[i]  
  54.                 if(temp>result[i])  
  55.                 {  
  56.                     result[i]=temp;  
  57.                     path[i]=k;  
  58.                 }  
  59.             }  
  60.         }  
  61.     }  
  62.     //打印结果  
  63.     void display()  
  64.     {  
  65.         cout<<"最大*,+结果:"<<result[num]<<endl;  
  66.         printPath(num);  
  67.     }  
  68.     //递归打印整个公式  
  69.     void printPath(int n)  
  70.     {  
  71.         if(path[n]==1)  
  72.         {  
  73.             int j=1;  
  74.             while(j<=n)  
  75.             {  
  76.                 cout<<arr[j];  
  77.                 if(j<n)  
  78.                 {  
  79.                     cout<<"*";  
  80.                 }  
  81.                 ++j;  
  82.             }  
  83.             return;  
  84.         }  
  85.   
  86.         printPath(path[n]-1);  
  87.         cout<<"+";  
  88.         int i=path[n];  
  89.         while(i<=n)  
  90.         {  
  91.             cout<<arr[i];  
  92.             if(i<n)  
  93.             {  
  94.                 cout<<"*";  
  95.             }  
  96.             ++i;  
  97.         }  
  98.     }  
  99. };  
  100.   
  101. void main()  
  102. {  
  103.     Sum sum(6); // 2 1 1 1 1 2  
  104.     sum.input();  
  105.     sum.maxResult();  
  106.     sum.display();  
  107. }  

 

 

 

思想是动态规划求解 ,

公式: result[n]=max { (x[k]*x[k+1]*x[k+2]*....x[n])+result[k-1] } ,k取1....n  

result[n]表示1...n的最大值

参考的 zyl072 的思路 ,重点理解 X*X*X + X*X*X*X + X + X*X   

我们可以从2 1 1 1 1 2的最后往前找,找到一个位置放置一个加号,从这里断开,+ 的右边的是乘积运算 ,右边的乘积+左边的最大值就是整个序列最大值。 左边最大值是同样的子问题 ,会发现有最优子结构性质,动态规划递推就可以了。

关键就是单独的一个X也符合这个公式, 例如2 1 1 1 1 2, 我们可以把加号放在任何一个空位,然后对+右边做乘,对+左边继续递归计算。 例如 2 1 1 1 1 + 2, 这种情况是只有2自己做乘法运算,+左边是同样问题 2 1 1 1 1 ,且与父问题无关 ,这时候举个例子 2 + 1 1 1 1,右边4个1相乘,然后对2递归,2只有自己相乘了就是它本身2 ,公式就变成了 2+1*1*1*1+2 。 现在去看看公式就明白最优子结构了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值