问题描述:
程序思路:
我不知道什么是优化子结构及子问题重叠性质,完全凭自己感觉写的思路。
设dp[i]表示a[1]到a[i]这个整数序列的所有子序列最大值的最小化之和。
因此,有
为了方便计算,可设dp[0]=0;可由dp[i+1]的公式计算出dp[1,2,…,8],最后的dp[8]即为结果。
程序代码(C++):
#include <iostream>
#include <vector>
const int getNumberMax(const int a, const int b) //get the max(a,b)
{
return (a > b)? a : b;
}
const int getSumMin(const int a, const int b) //get the min(a,b)
{
return (getNumberMax(a, b) == b)? a : b;
}
int main()
{
using std::cout;
using std::endl;
using std::cin;
using std::vector;
vector<int> ivec = { 2, 2, 2, 8, 1, 8, 2, 1 }; //the array a[N]
int len = static_cast<int> (ivec.size()); //the length of a[]
const int LIM = 17; //the limit
vector<int> dp = {0}; //the array of dp[] and dp[0]=0;
for(int i = 1; i < len + 1; ++i)
{
int j = i - 1;
//max to store the max of subarray before a[i](includea[i]). The sum of subarray<=LIM
int max = ivec[j];
//sumTemp to store the sum of subarray
int sumTmp = ivec[j];
//sumMin to store every value maybe be dp[i]
int sumMin = ivec[j] + dp[j];
while(j > 0 && (sumTmp + ivec[j]) <= LIM)
{
sumTmp+= ivec[j-1];
max=getNumberMax(max, ivec[j-1]);
sumMin= getSumMin(sumMin, dp[j-1] + max);
--j;
}
dp.push_back(sumMin);
}
for(int i : dp) //to output dp[0...len]
{
cout<< i << ' ';
}
cout<< endl;
//to output the result
cout<< "The result is " << *(dp.end() - 1) << endl;
cin.get();
return 0;
}
至此,这题已经解决。接下来,分享一下我的做题过程(选读),有兴趣的,可以看一下。
这是实验室的一个师弟问我的题目。当时,一看是动态规划(dynamic plan,DP)的题目。还是有点畏惧,不过所幸题目并不长,我就耐心的读了下来。第一感觉,就是题目不怎么难,我应该能做出来。很快我有了思路,但应该不是动态规划的思路。有点像递归,逐渐缩小规模来做。
接下来,就带着一点懵懂,开干了。编写的过程中,问题不断。情况二出现问题,不能化成相同的子问题。怎么办?好歹也看这么久的书,虽然不是关于算法的,但语言的技巧都还能掌握的。带着一点不服,继续思考。第二天,利用琐碎时间想到解决方法,完成正常递归。
Sub_Max_Min()功能,求vector v[pos]之后len长度的最小化子序列最大值之和的解,也就原问题的解==Sub_Max_Min(ivec,0,ivec.size(),LIM)
Sub()则是考虑左边存在一个子序列,子序列之和为lsum,最大值为lmax,在这个约束条件下,考虑原问题的解。
写完程序吓了一跳,好复杂的感觉。有两个函数,这让我觉得肯定能优化一下。于是,再想了几天,终于化成一个函数。
其实,
Sub_Max_Min(ivec,0,ivec.size(),LIM)=Sub(0,0,ivec,0,ivec.size(),LIM)
于是将二者归一为
int Sum_Max_Min(int lsum, int lmax, const vector<int>& ivec, intpos, int len, const int LIM)
(具体代码见附件1)
接下来,感觉我这种思路无法继续优化下去。于是,往DP上面去想。Dp常用的方法已经全忘了,只记得要找转换公式,于是找了《王道论坛计算机考研机试指南》,看了下dp那章,仔细看了最长递增子序列(LIS)和最长公共子序列(LCS)。之后,对dp的思路形式大体有点掌握,开始继续这个问题。终于当晚来了灵感。第二天,早上立马写出程序。写完,真是日*了---狗了。好简单!思路也很清晰,比我当初的算法简单很多,而且高效。突然,觉得这题好简单,我突然耗了这么多时间,顿时想对自己说了n句傻*。
(具体代码见附件2)
总结与扩展:
- 题目总结 (1)最小化->最大值的和 (2)子序列之和不超过 T (这个条件貌似不好改变)
- 题目扩展
- 最大化-》最大值的和 没啥意思 都是单个子序列
- 最大化-》最小值的和 没啥意思 都是单个子序列
- 最小化-》最小值的和 这个可以用贪心来做
- 进一步扩展思路: 如果数组里面存在负数如何解决呢; 子序列个数如果存在个数限定呢。考虑加入对异常的处理,比如a[i]>LIM,如何处理。
附件1
#include <vector>
#include <iostream>
usingnamespace std;
typedef vector<int>::size_type size;
long Sub_Max_Min(const vector<int>&, int, size,const int);
long Sub(int lsum,int lmax,const vector<int>& vec,int pos, size len,const int LIM);
int main()
{
int n;
cin >> n;
vector<int> ivec;
//input the array of A[]s
for(int i= 0; i!= n;++i)
{
int num;
cin >> num;
ivec.push_back(num);
}
//input the limitation of B
int lim;