[I0A]查找最大子串问题的求解
2016-02-12 22:33
405 查看
Question:
假如你能够预知股票未来几天的动向,请你选择合适的买入和售出 日期 以便获得最大的利益值?
如下为股票未来几天的变动情况:
Result:
最优解为第 7 天买入,第11天卖出 获得最大单元利益为 43.
Analysis:
对于这个问题的求解我们当然可以用传统的排列组合方法来求解,即从以上任意天数中选择两天来进行计算
比如 选择day 3 和 day 9 101- 85 = 16 单元利益为16
所有的排列组合为 N*(N-1) / 2 时间复杂度为O(n^2)
然而此题依然可以有更效率的方式求解出结果,最终时间复杂度为O(n lg n).
如表格第三栏 change行,我们知道 要活得最大利益 即要求 第三行中 的 求得 最大子串 即 18 + 20 -7 +12 = 43
所以该问题转化为求解 最大子串问题.
How:
如何求解最大子串问题?这里我们仅使用 分治法(Divide and Conquer)求解
分治法分为三个步骤:Divide,Conquer,Combine.
Divide 将一个大的问题划分成若干个小的同类型的问题
Conquer 对划分后的小问题进行求解
Combine 将求解后的小问题结果合并 得出最后大问题的结果
显然 如果要将这一长串数列 求出最大子串 将这长子列化分成若干个子列 然后对子列进行求解 然后将结果合并.
我们将数列分成两个子串 即A[beg] ~ A[mid] 和 A[mid+1] ~ A[end] 两个部分
假设最大子串 出现在这两个部分,我们只要求解被划分后的两个子串
所以应该分为三部分
1.beg<= i <= j <= mid
2.mid+1<= i <= j <= end
3.beg <= i <= mid < j <= end
首先我们针对于分界处来进行计算
首先计算分界点左部分子串最大值,同时保存子串最大值时的标号index
然后计算分界点右边子串最大值,同时保存子串最大值时的标号
然后返回左右部分合并后的子串最大值
整个流程思路为计算左子串最大值,然后再计算右边子串最大值,然后从中间向两边计算交界处子串最大值 如下图所示
附上源代码
测试结果如下图:
假如你能够预知股票未来几天的动向,请你选择合适的买入和售出 日期 以便获得最大的利益值?
如下为股票未来几天的变动情况:
Result:
最优解为第 7 天买入,第11天卖出 获得最大单元利益为 43.
Analysis:
对于这个问题的求解我们当然可以用传统的排列组合方法来求解,即从以上任意天数中选择两天来进行计算
比如 选择day 3 和 day 9 101- 85 = 16 单元利益为16
所有的排列组合为 N*(N-1) / 2 时间复杂度为O(n^2)
然而此题依然可以有更效率的方式求解出结果,最终时间复杂度为O(n lg n).
如表格第三栏 change行,我们知道 要活得最大利益 即要求 第三行中 的 求得 最大子串 即 18 + 20 -7 +12 = 43
所以该问题转化为求解 最大子串问题.
How:
如何求解最大子串问题?这里我们仅使用 分治法(Divide and Conquer)求解
分治法分为三个步骤:Divide,Conquer,Combine.
Divide 将一个大的问题划分成若干个小的同类型的问题
Conquer 对划分后的小问题进行求解
Combine 将求解后的小问题结果合并 得出最后大问题的结果
显然 如果要将这一长串数列 求出最大子串 将这长子列化分成若干个子列 然后对子列进行求解 然后将结果合并.
我们将数列分成两个子串 即A[beg] ~ A[mid] 和 A[mid+1] ~ A[end] 两个部分
假设最大子串 出现在这两个部分,我们只要求解被划分后的两个子串
所以应该分为三部分
1.beg<= i <= j <= mid
2.mid+1<= i <= j <= end
3.beg <= i <= mid < j <= end
首先我们针对于分界处来进行计算
result find_max_crossing_subarrary(int* arr,int beg,int mid,int end){ int left_sum = INT_MIN,right_sum = INT_MIN; int left_index,right_index; int sum = 0; int i; for(i = mid;i >= beg; i--){ sum = sum + arr[i]; if(sum > left_sum) { left_sum = sum; left_index = i; } } sum = 0; for(i = mid+1;i <= end;i++){ sum = sum + arr[i]; if(sum > right_sum) { right_sum = sum; right_index = i; } } result res = {left_index,right_index,left_sum + right_sum}; return res; }
首先计算分界点左部分子串最大值,同时保存子串最大值时的标号index
然后计算分界点右边子串最大值,同时保存子串最大值时的标号
然后返回左右部分合并后的子串最大值
整个流程思路为计算左子串最大值,然后再计算右边子串最大值,然后从中间向两边计算交界处子串最大值 如下图所示
附上源代码
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
typedef struct {
int beg;
int end;
int sum;
} result;
void
arr_print(int *arr,int len);
int*
arr_convertor(char **argv,int *len);
int*
max_difference_arr(int *arr,int len);
result
find_max_crossing_subarrary(int* arr,int beg,int mid,int end);
result
find_max_subarrary(int *arr,int beg,int end);
/*
day 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
p 100 113 110 85 105 102 86 63 81 101 94 106 101 79 94 90 97
c 0 13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7
*/
int main(int argc,char **argv){
int len;
int *arr = arr_convertor(argv,&len);
if(len < 2) {
printf("arguments must more than 2.\n");
return 0;
}
arr_print(arr,len);
int *dif_arr = max_difference_arr(arr,len);
arr_print(dif_arr,len-1);
result res = find_max_subarrary(dif_arr,0,len-2);
printf("max subarray begins at:%d ends at:%d.\n",res.beg,res.end+1);
printf("and the max sum of the subarray is %d.\n",res.sum);
printf("the max array is:");
int i = 0;
for(i = res.beg;i <= res.end+1;i++) {
printf("%4d",arr[i]);
}
free(arr);
free(dif_arr);
return 0;
}
result find_max_subarrary(int *arr,int beg,int end){
if(beg == end) {
result res = {beg,end,arr[beg]};
return res;
}
int mid = (beg + end)/2;
result left_res = find_max_subarrary(arr,beg,mid);
result right_res = find_max_subarrary(arr,mid+1,end);
result cross_res = find_max_crossing_subarrary(arr,beg,mid,end);
if(left_res.sum >= right_res.sum && left_res.sum > cross_res.sum)
return left_res;
else if (right_res.sum >= left_res.sum && right_res.sum >= cross_res.sum)
return right_res;
else return cross_res;
}
result find_max_crossing_subarrary(int* arr,int beg,int mid,int end){ int left_sum = INT_MIN,right_sum = INT_MIN; int left_index,right_index; int sum = 0; int i; for(i = mid;i >= beg; i--){ sum = sum + arr[i]; if(sum > left_sum) { left_sum = sum; left_index = i; } } sum = 0; for(i = mid+1;i <= end;i++){ sum = sum + arr[i]; if(sum > right_sum) { right_sum = sum; right_index = i; } } result res = {left_index,right_index,left_sum + right_sum}; return res; }
int* max_difference_arr(int *arr,int len){
int* dif_arr = malloc(sizeof(int) * (len-1));
int i;
for(i = 0; i <len-1;i++){
dif_arr[i] = arr[i+1] - arr[i];
}
return dif_arr;
}
int* arr_convertor(char **argv,int *len){
int i;
char **ptr = argv;
int tmp[100];
while (*++ptr != NULL) {
tmp[i++] = atoi(*ptr);
}
*len = i;
int *parr = malloc(sizeof(int)*i);
for (i = 0; i < *len; i++) {
parr[i] = tmp[i];
}
return parr;
}
void arr_print(int *arr,int len){
int i;
for(i = 0;i < len;i++) printf("%4d",arr[i]);
printf("\n");
}
测试结果如下图:
相关文章推荐
- macbook使用“终端”远程登录linux主机
- macbook使用“终端”远程登录linux主机
- 1031. Hello World for U (20)
- Light OJ 1169 - Monkeys on Twin Tower (简单DP)
- Python教程学习简记9--Python 返回函数 闭包
- Android批量图片加载经典系列——Volley框架实现多布局的新闻列表
- 面试笔试杂项积累-leetcode 241-245
- android 入门 005(登录记住)
- 关于Struts2三种访问Servlet API方式的总结
- 2016/2/12 codes
- 在android平台hook OpenGL es的API
- Activity以及fragment的简单重写
- ROS(机器人操作系统)学习(一)ROS开发环境搭建
- 面试笔试杂项积累-leetcode 236-240
- ANDROID_MARS学习笔记_S01原始版_001_Intent
- java集合中hashSet与TreeSet学习总结
- [小技巧] Linux 里快速安装缺少的库
- LeetCode Algorithms #13 <Roman to Integer>
- android 截图
- C语言指针赋值0会产生什么结果?