题目
给定由
n
个
整
数
(
可
能
有
负
数
)
组
成
的
序
列
a
1
,
a
2
,
.
.
.
,
a
n
,
要
在
这
n
个
数
中
选
取
相
邻
的
一
段
a
i
,
a
i
+
1
,
.
.
.
,
a
j
(
1
≤
i
≤
j
≤
n
)
,
使
其
和
最
大
,
输
出
最
大
的
和
。
当
所
有
整
数
均
为
负
整
数
时
,
定
义
最
大
字
段
和
为
0
。
n个整数(可能有负数)组成的序列a_1,a_2,...,a_n,要在这n个数中选取相邻的一段a_i,a_{i+1},...,aj(1\leq i \leq j \leq n),使其和最大,输出最大的和。当所有整数均为负整数时,定义最大字段和为0。
n个整数(可能有负数)组成的序列a1,a2,...,an,要在这n个数中选取相邻的一段ai,ai+1,...,aj(1≤i≤j≤n),使其和最大,输出最大的和。当所有整数均为负整数时,定义最大字段和为0。
例如,当
{
a
1
,
a
2
,
.
.
.
,
a
8
}
\{a_1,a_2,...,a_8\}
{a1,a2,...,a8} = {1,-3,7,8,-4,12,-10,6}时,最大字段和为23。令
b
[
j
]
=
max
1
≤
i
≤
j
{
∑
k
=
i
j
a
[
k
]
}
(
1
≤
j
≤
n
)
,
b[j] =\max_{1 \leq i \leq j} \{ \sum_{k=i}^j a[k]\} (1 \leq j \leq n),
b[j]=1≤i≤jmax{k=i∑ja[k]}(1≤j≤n),则所求的最大字段和为:
max
1
≤
i
≤
j
≤
n
{
∑
k
=
i
j
a
[
k
]
}
=
=
max
1
≤
j
≤
n
{
max
1
≤
i
≤
j
{
∑
k
=
i
j
a
[
k
]
}
}
=
max
1
≤
j
≤
n
{
b
[
j
]
}
\max_{1 \leq i \leq j \leq n} \{ \sum_{k=i}^j a[k]\} == \max_{1 \leq j \leq n} \{ \max_{1 \leq i \leq j} \{\sum_{k=i}^j a[k]\} \} = \max_{1 \leq j \leq n} \{ b[j]\}
1≤i≤j≤nmax{k=i∑ja[k]}==1≤j≤nmax{1≤i≤jmax{k=i∑ja[k]}}=1≤j≤nmax{b[j]}
其实上面定义看不懂并不影响理解,看懂下面的递归关系即可,上面写着只是为了显得更专业,好吧!我承认是课本的定义。
建立递归关系
当
b
[
j
−
1
]
>
0
时
,
b
[
j
]
=
b
[
j
−
1
]
+
a
[
j
]
,
否
则
b
[
j
]
=
a
[
j
]
。
b[j-1]>0时,b[j] = b[j-1]+a[j],否则b[j] = a[j]。
b[j−1]>0时,b[j]=b[j−1]+a[j],否则b[j]=a[j]。这句话还是十分简单的,要是前面的和大于0,那最大和一定要加上前面的正数。反之,若小于0则必须弃之。
b[j]状态转移方程为:
b
[
j
]
=
max
{
b
[
j
−
1
]
+
a
[
j
]
,
a
[
j
]
}
,
(
1
≤
j
≤
n
)
b[j] =\max \{ b[j-1] + a[j],a[j] \}, (1 \leq j \leq n)
b[j]=max{b[j−1]+a[j],a[j]},(1≤j≤n)
代码
#include <stdio.h>
#define NUM 1001
int a[NUM];
int MaxSum(int n, int &besti, int &bestj)
{
int sum=0;
int b=0;
int begin = 0;//当b[i-1]小于等于0时,记录b[i]=a[i]的位置
for (int i = 1;i <= n;i++)
{
if (b>0) b+=a[i]; //状态转移方程
else {//当b<0时,更新到最新的位置
b=a[i];
begin = i;//开始位置
}
if (b>sum)
{
sum = b; //更新最优值
besti = begin;
bestj = i;
}
}
return sum;
}
int main()
{
int n;
int besti, bestj;
while (scanf("%d", &n) && n)
{
besti = 0; //初始化开头
bestj = 0; //初始化结尾
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
printf("%d\n", MaxSum(n, besti, bestj));//输出最优值
printf("From %d to %d\n", besti, bestj);//输出最优解
}
return 0;
}
输入
8
1 -3 7 8 -4 12 -10 6
0
输出
23
From 3 to 6
总结
最大子矩阵和问题是最大字段和问题的推广,首先在这埋下一个坑,日后若遇到在加以学习吧!