这是两道题,不过对于从一维走向二维的思想启发很有帮助,可以借鉴。
最大子段和
题意:
一个数列,从中抽一串连续数,数字和最大。(数据包含负数)
输入:
输入数字个数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;
}