最大字段和+二维最大矩阵和

这是两道题,不过对于从一维走向二维的思想启发很有帮助,可以借鉴。

最大子段和

题意:

一个数列,从中抽一串连续数,数字和最大。(数据包含负数)

输入:

输入数字个数n,然后输入一列数。

(草率的说明)

暴力做法三次方,枚举左右端点然后加起来。

用前缀和可以优化成平方,枚举左右端点。

但数据范围是1e6 qwqwq

我推荐O(n)做法:动态规划(此处约等于贪心)

我们设f[i]表示结尾是i号数字的最大字段和。

那除了第一个,对于任何一个f[i],它要么加上前面的最大字段和,要么自己独立成段。

那判断条件是什么呢?f[i-1]>0。

如果前面的最大字段和大于0,那你当然可以把当前数加上前面的字段。但如果前面是0或者负数,那不加自然最好。

所以说f[i]=(f[i-1]>0)?f[i-1]+a[i]:a[i]。

最后记得sort一遍搞出最大的那个。

记得初始化第一个哦。

附代码。

​
#include<bits/stdc++.h>
#define in read()
using namespace std;
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();
		if(ch=='-')
		f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}
	return cnt*f;
}
int n;
long long f[200003];
int a[200003];
int main(){
	n=in;
	for(int i=1;i<=n;i++){
		a[i]=in;
	}
	f[1]=a[1];
	for(int i=2;i<=n;i++){
		if(f[i-1]>=0)f[i]=(long long)a[i]+f[i-1];
		else f[i]=(long long)a[i];
	}
	
	sort(f+1,f+n+1);cout<<f[n];
	return 0;
}

​

第一个记得初始化哦。

下面进入二维。

二维最大矩阵

题意:给一个数字矩阵,求最大的一个子矩阵。

输入:n排,m列,n*m个数。

输出:当然是最大的和啦。

可以看出朴素枚举算法是四次方。那太可怕了,数据范围是500。

这里给读者一个提示:

想想一维如何变成二维,如何在一维的基础上增加一个可枚举的维度。

 

 

这里继续。

矩阵最大的特点是,你不能一个一个从头开始推,因为你不确定之前的最大子矩阵的图形样子。

既然如此,我们就从一维扩张入手。

一维无论如何也只能搞完一排

但如果,这一排比较“胖”,胖成了一个矩阵呢?

一个矩阵,我们假设它占了k排,但宽不知道。

那我们就把这k排每一列的k个数字加起来当成一个数,将这个k*m的矩阵变成一个m长度的数列。这个数列你用一个前缀和就可以轻松解决。

现在就直接套最长字段就可以啦!

可以看出,我们在一维的基础上,暴力枚举这k排的位置以及k的大小,然后对于每个情况进行一次最长字段,打擂台取max,得到的就是答案啦。

复杂度n³(枚举开头点,结尾点,计算字段)

上代码。

#include<bits/stdc++.h>
#define in read()
using namespace std;
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();
		if(ch=='-')
		f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}
	return cnt*f;
}
int n,m;
long long a[502][502];
long long f[502];
long long b[502];
long long sum[502][502];
long long go(int i1,int i2){
	b[1]=sum[1][i2]-sum[1][i1-1];
	f[1]=(b[1]>0)?b[1]:0;
	for(int i=2;i<=n;i++){
		b[i]=sum[i][i2]-sum[i][i1-1];
		f[i]=b[i];
		if(f[i-1]>0)f[i]+=f[i-1];
	}
	sort(f+1,f+n+1);
	return f[n];
}
long long maxx=-0x7fffffffffffffff;
int main(){
	n=in;m=in;
	long long cnt=-0x7fffffffffffff,flag=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%lld",&a[i][j]);
			if(a[i][j]>0)flag=1;
			cnt=max(cnt,a[i][j]);
			sum[i][j]=a[i][j]+sum[i][j-1];
		}
	}
	if(!flag){cout<<cnt;return 0;}//这是一步特判,小心如果全是负数,容易卡出问题。
	for(int i1=1;i1<=m;i1++){
		for(int i2=i1;i2<=m;i2++){//枚举开排点和结排点
			long long now=0;
			for(int k=1;k<=n;k++){//这个k没有上文的意思
				now+=sum[k][i2]-sum[k][i1-1];
				if(now<0)now=0;
				maxx=max(maxx,now);
//更简单的最大子段和求法,可以让此处复杂度从2n变成n,否则要T6个点。
			}
		}
	}
	cout<<maxx;
	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 数字三角形问题:给定一个数字三角形,从顶部出发,每次只能移动到相邻的数字,求从顶部到底部路径上所有数字之和的最大值。 - 分析:可以使用动态规划来解决该问题,从底部开始递推,每个位置上的最优解为当前位置的值加上下一行相邻的两个数字中较大的那个。最终得到从顶部到底部路径上所有数字之和的最大值。 2. 矩阵链乘问题:给定一系列矩阵,求它们相乘的最小代价。 - 分析:可以使用动态规划来解决该问题,设计一个二维数组来存储每个矩阵链的最优解,递推公式为dp[i][j] = min(dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j]),其中p[i-1]表示第i个矩阵的行数,p[i]表示第i个矩阵的列数,k为i到j之间的分割点。 3. 最长公共子序列问题:给定两个序列,求它们的最长公共子序列的长度。 - 分析:可以使用动态规划来解决该问题,设计一个二维数组来存储两个序列中每个子序列的最优解,递推公式为dp[i][j] = dp[i-1][j-1] + 1(当序列i和序列j的最后一个元素相同时),dp[i][j] = max(dp[i-1][j], dp[i][j-1])(当序列i和序列j的最后一个元素不同时)。 4. 最大字段和问题:给定一个序列,求它的连续子序列中,所有元素之和的最大值。 - 分析:可以使用动态规划来解决该问题,设计两个变量来存储当前的最大字段和和最大字段的起始位置,递推公式为sum[i] = max(sum[i-1]+a[i], a[i]),其中sum[i]表示以a[i]结尾的最大字段和。 5. 0-1背包问题:给定一组物品,每个物品有一个重量和一个价值,在限定的背包容量内,选择哪些物品可以使得它们的总价值最大。 - 分析:可以使用动态规划来解决该问题,设计一个一维数组来存储当前背包容量下的最大价值,递推公式为dp[j] = max(dp[j], dp[j-w[i]]+v[i]),其中dp[j]表示背包容量为j时的最大价值,w[i]表示第i个物品的重量,v[i]表示第i个物品的价值。 6. 矩形嵌套问题:给定一些矩形,判断是否存在一种嵌套方式,使得所有矩形都被嵌套在其中一个矩形中。 - 分析:可以先按照长或宽进行排序,然后使用动态规划来解决该问题,设计一个一维数组来存储每个矩形的嵌套深度,递推公式为dp[i] = max(dp[j])+1(当第i个矩形可以嵌套在第j个矩形内部时),dp[i] = 1(当第i个矩形无法嵌套在其他矩形内部时)。最终判断是否存在一个矩形的嵌套深度等于矩形的个数即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值