分治策略
4.0
分治策略用递归的方式求解问题,每层递归包括下面三个基本步骤:
- 分解: 将问题分解成若干个子问题,子问题形式与原问题相同,但规模较小。
- 解决: 递归地求解子问题,若子问题规模足够小,则停止递归,直接求解。
- 合并; 将子问题的解合并成原问题的解。
递归式的三种求解方法:
- 代入法: 猜测+数学归纳法证明。
- 递归树法: 将递归式转换为一棵树,节点表示递归调用产生的代价。然后用边界和技术求解。
- 主方法; 可求解形如下面公式的递归式的界:
T ( n ) = a T ( n / b ) + f ( n ) T(n)=aT(n/b)+f(n) T(n)=aT(n/b)+f(n)
4.1
题目: 给出一支股票n天内的价格,可以在某一天买入一股,并在之后的某天卖出,求可以获得的最大收益是多少。
暴力穷举的时间复杂度为:
Θ
(
n
2
)
Θ(n^2)
Θ(n2)
问题变换:
由输入的价格序列
P
[
0..
n
]
P[0..n]
P[0..n]可生成一个新序列
A
[
1...
n
]
A[1...n]
A[1...n],其中
A
i
A_i
Ai表示第
i
i
i天和第
i
−
1
i-1
i−1天的差价。则问题转换为:求出
A
A
A的一个连续子序列,是其和最大(股票价格净变化值最大)。这样的子数组被称为:最大子数组。
分支策略求解:
- 分解: 将数组 A [ p . . . q ] A[p...q] A[p...q]从中间分解成两个子数组 L [ p . . . m i d ] L[p...mid] L[p...mid]和 R [ m i d + 1... q ] R[mid+1. ..q] R[mid+1...q]。
- 解决: 当子数组长度为1时,该子数组的最大子数组即为其自身。
- 合并: 对于数组
A
[
p
.
.
.
q
]
A[p...q]
A[p...q]其最大子数组有三种情况:
- 全部位于 L [ p . . . m i d ] L[p...mid] L[p...mid]中全部位于 L [ p . . . m i d ] L[p...mid] L[p...mid]中。
- 全部位于 R [ m i d + 1... q ] R[mid+1...q] R[mid+1...q]中全部位于 R [ m i d + 1... q ] R[mid+1...q] R[mid+1...q]中。
- 跨越 m i d mid mid一半在 L L L中,一半在 R R R中跨越 m i d mid mid一半在 L L L中,一半在 R R R中。
代码如下:
/*最大子序列*/
#define _CRT_SECURE_NO_WARNINGS
#define MAXN 9999999999
#include<stdio.h>
long long int cal(long long int a[], long long int p, long long int q)
{
long long int m = (p + q) / 2, max_l, max_r, max_m, max_m_l, max_m_r, i, sum, ans;
if (p == q) return a[p];
max_l = cal(a, p, m);
max_r = cal(a, m + 1, q);
max_m_l = -MAXN;
sum = 0;
for (i = m; i > 0; i--)
{
sum += a[i];
if (sum > max_m_l)
max_m_l = sum;
}
max_m_r = -MAXN;
sum = 0;
for (i = m + 1; i <= q; i++)
{
sum += a[i];
if (sum > max_m_r)
max_m_r = sum;
}
max_m = max_m_l + max_m_r;
ans = max_l > max_r ? max_l : max_r;
if (max_m > ans)
ans = max_m;
return ans;
}
void main()
{
long long int n, i, ans, a[100000];
scanf("%lld", &n);
for (i = 1; i <= n; i++)
scanf("%lld", &a[i]);
/*解决股票问题(读入循环改为 i = 0 to n-1;下面 cal() 函数 n 改为 n-1;)
for (i = n - 1; i > 0; i--)
a[i] = a[i] - a[i - 1];
*/
printf("%lld\n", cal(a, 1, n));
getchar();
getchar();
return;
}