最大连续子序列→最大子矩阵

最大连续子序列是一道经典的问题,如果你只是求最大连续子序列的和

 

题如: -2, -1, -3, -4, -2五个数组成的序列,求其最大子序列之和。

 

下面这个就可以了:

​dp[i] = max(dp[i-1]+a[i], a[i]);

但如果你想输出最大连续子序列呢?

如果你用上面的式子,会发现无法对其的位置进行记录。

 

故我们采用如下方式:

int max = a[1], sum = 0, first = 1, end = 1, temp = 1;//初始化均为第一项的值
for(i = 1; i <= n; i++){
    sum = sum + a[i]; //每次叠加
    if(sum > max){ //当sum大于之前的最大值
        max = sum;//这时的sum就是新最大值
        first = temp;//如果sum不小于0,first将不更新
        end = i;//每次max更新了,则end也会后移
    }
    if(sum < 0){//如果sum<0,(sum与后续子序列的和) 必将小于 (后续子序列和的本身)
        sum = 0;//新的开始,将其置0
        temp = i+1;//temp也会更新,不过first更新与否将取决于后面的子序列和是否大于max
    }
}

这时候我们便能保证在任何情况下都能成立,你可以肆意的在循环结束后输出最大子序列的所有元素。

 

 

那么重点来了,最大子矩阵和怎么解决呢。

与上面的有些许不同,我们先将输入的所有数用另一个数组保存下来

之后进行矩阵的行列相加,使得到的新矩阵包含了其左边和上方的所有元素,作为所有元素之和(包括其本身)

那么就是这步了num[i][j]是输入的元素,all是每一步后的行列之和得到的新元素

for(i = 1; i <= n; i++)
		for(j = 1; j <= n; j++)
			all[i][j] = all[i-1][j] + num[i][j];
	for(j = 1; j <= n; j++)
		for(i = 1; i <= n; i++)
			all[i][j] = all[i][j-1] + all[i][j];

这时候你肯定会想到这只是一部分的子矩阵,还有那些块块的矩阵呢?

那么我们就会通过一系列的循环相减 使该位置元素所包括其之上与其之左的元素和达到最大值(包括只有一种元素(即该原本输入时就处于该位置的元素)的情况)。

但是不好理解的就是这个相减的情况

如果看不懂可以戳下面的大佬链接。

大佬关于最大子矩阵的讲解,非常形象易懂,看了那么多的博客只有这个通过图片真的明白了。

https://blog.csdn.net/sigd/article/details/82876773

 

贴一个zoj1074的AC代码

#include<stdio.h>
int num[1005][1005];
int all[1005][1005];
int m[1005][1005];
int main()
{
	int n, i, j, k, l;
	scanf("%d", &n);
	for(i = 1; i <= n; i++)
		for(j = 1; j <= n; j++){ 
			scanf("%d", &num[i][j]);
			m[i][j] = num[i][j];
		} 
	
	int max = num[1][1];
	for(i = 1; i <= n; i++)//行相加
		for(j = 1; j <= n; j++)
			all[i][j] = all[i-1][j] + num[i][j];
	for(j = 1; j <= n; j++)//列相加
		for(i = 1; i <= n; i++)
			all[i][j] = all[i][j-1] + all[i][j];
	//此时行列和全部完成

	for(i = 1; i <= n; i++){
		for(j = 1; j <= n; j++){
			for(k = 1; k <= i; k++){//起点
				for(l = 1; l <= j; l++){//终点
                   //逐渐缩小矩阵范围
					int t = all[i][j] - all[k-1][j] - all[i][l-1] + all[k-1][l-1];
					if(t > m[i][j])
						m[i][j] = t;
					if(m[i][j] > max)
						max = m[i][j];
				}
			}
		}
	}
	printf("%d\n", max);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值