最大子序列和
问题描述
给定一个有 n (n≥1) 个整数的序列,要求求出其中最大连续子序列的和。
例如:
- 序列 (-2,11,-4, 13,-5,-2) 的最大子序列和为 20
- 序列 (-6,2,4,-7,5,3,2,-1,6,-9,10,-2) 的最大子序列和为16。
规定一个序列最大连续子序列和至少是0 (长度为0的子序列),如果小于0,其结果为0。
分治策略
若 n>1,采用分治法求解最大连续子序列时,取其中间位置 mid=(n-
1)/2, 该子序列只可能出现3个地方。
- 该子序列完全落在左半部即 a[0, …, mid] 中。采用递归求出其最大
连续子序列和 leftMaxSum.- 该子序列完全落在右半部即 a[mid + 1, …, n - 1] 中。采用递归求出其最大连续子序列和 rightMaxSum.
- 该子序列跨越序列a的中部而占据左右两部分。
算法实现
#include <iostream>
using namespace std;
int max3(int a, int b, int c);
int maxsubSum(int a[], int low, int high);
int main(){
int a[] = {11,-4,13, -5,-2};
cout << maxsubSum(a, 0, 4);
return 0;
}
int max3(int a, int b, int c){
return a > b ? (a > c ? a : c) : (b > c ? b : c);
}
int maxsubSum(int a[], int low, int high){
if(low == high)
if(a[low] > 0)
return a[low];
else
return 0;
// 递归求解
int mid = (low + high) / 2;
int leftMaxSum = maxsubSum(a, low, mid); //左部最大和
int rightMaxSum = maxsubSum(a, mid + 1, high); //右部最大和
// 从中间开始,向两边分别计算最大和并相加得出 low 和 high 之间的最大和
int leftMarginMaxSum = 0;
int rightMarginMaxSum = 0;
int leftMarginSum = 0;
int rightMarginSum = 0;
for(int i = mid; i >= low; i--){
leftMarginSum += a[i];
if(leftMarginMaxSum < leftMarginSum)
leftMarginMaxSum = leftMarginSum;
}
for(int i = mid + 1; i <= high; i++){
rightMarginSum += a[i];
if(rightMarginMaxSum < rightMarginSum)
rightMarginMaxSum = rightMarginSum;
}
int marginMaxSum = leftMarginSum + rightMarginSum;
return max3(leftMaxSum, rightMaxSum, marginMaxSum);
}
个人想法:
marginMaxSum = leftMarginSum + rightMarginSum
求 " 整个部分 " 的子序列最大和应该算是关键了。
循环日程安排
问题描述
设有 n = 2k 个选手要进行网球循环赛,要求设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次。
(2)每个选手一天只能赛一次。
(3)循环赛在n-1天之内结束。
分治策略
引子
- k = 1
计划表
| 选手号 | |
| 1 | 2 |
| 2 | 1 |
| 选手号 | |||
| 1 | 2 | 3 | 4 |
| 2 | 1 | 4 | 3 |
| 3 | 4 | 1 | 2 |
| 4 | 3 | 2 | 1 |
- k = 3
计划表选手号 1 2 3 4 5 6 7 8 2 1 4 3 6 5 8 7 3 4 1 2 7 8 5 6 4 3 2 1 8 7 6 5 5 6 7 8 1 2 3 4 6 5 8 7 2 1 4 3 7 8 5 6 3 4 1 2 8 7 6 5 4 3 2 1
1、k = 1 时的日程表是 k = 2 时日程表的左上部分,k = 2 时的日程表是 k = 3 时日程表的左上部分,····,k = n - 1 是的日程表是 k = n 时的日程表的左上部分
2、每个日程表的左上部分与右下部份相同,左下部分与右上部份相同
3、左上部分与左下部分对应位置的值相差 2k-1
基于这些特点,给出分治策略:
分治策略
将 n=2k 问题划分为4部分:
(1)、 左上角: 左上角为 2k-1 个选手在前半程的比赛日程 ( k=1 时 直接给出,否则,上一轮求出的就是 2k-1 个选手的比赛日程 )。
(2)、 左下角: 左下角为另 2k-1 个选手在前半程的比赛日程,由左上角加 2k-1 得到,例如22个选手比赛,左下角由左上角直接加2 (2k-1)得到,23个选手比赛,左下角由左上角直接加4 (2k-1) 得到。
(3)、右上角: 将左下角直接复制到右上角得到另 2k-1 个选手在后半程的比赛日程。
(4)、右下角: 将左上角直接复制到右下角得到 2k-1 个选手在后半程的比赛日程。
算法
#include <iostream>
using namespace std;
#define MAX 100
int n; //比赛人数
int a[MAX][MAX]; //存放比赛日程表(行列下标为0的元素不用
// k 为2的幂数
void plan(int k){
n = 2; // n 从 2 ^ 1 = 2 开始
a[1][1] = 1;
a[1][2] = 2;
a[2][1] = 2;
a[2][2] = 1;
int t = 1;
while(t < k){
int temp = n;
n = n * 2;
// 左下角
for(int r = temp + 1; r <= n; r++){
for(int c = 1; c <= temp; c++)
a[r][c] = a[r - temp][c] + temp;
}
//右上角
for(int r = 1; r <= temp; r++){
for(int c = temp + 1; c <= n; c++)
a[r][c] = a[r + temp][(c + temp) % n];
}
//右下角
for(int r = temp + 1; r <= n; r++){
for(int c = temp + 1; c <= n; c++)
a[r][c] = a[r - temp][c - temp];
}
t++;
}
}
int main(){
plan(3);
//结果展示
for(int r = 1; r <= n; r++) {
for (int c = 1; c <= n; c++)
cout << a[r][c] << " ";
cout << endl;
}
return 0;
}
个人想法
理解右上角元素的求法可能有些难度。
本文深入解析分治法在解决最大子序列和问题及循环赛日程安排中的应用。通过实例代码展示了如何求解最大连续子序列的和,并设计满足特定条件的网球循环赛日程表。

3万+

被折叠的 条评论
为什么被折叠?



