最大字段和洛谷p1115 题解
写的比较多(good),承认自己比较菜,但是自认为还是写的挺细的,至少把我思考的全过程都写了。
看题
首先看题目,很好理解。
就是求一个数组中,最长的连续区间。
题解1
我们对这个字符串进行遍历,
代码如下:
#include <iostream>
using namespace std;
int main(){
static int N[200000];
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>N[i];
}
int s=0;
int S=0;
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
s=0;
for(int z=i;z<j;z++)
s+=N[z];
S=S>s?S:s;
}
}
cout<<S<<endl;
return 0;
}
这个代码的复杂度是O(n3),为什么呢?因为我们用i确定的是端点左侧,用j确定的是端点右侧,又加上这个区域里的所有的数,是三层循环。
结果:
显然,这样会超时,但是我们可以算出样例:
题解2
我们在算——例如2,-4和2,-4,3时,我们重复算了【2】+【-4】这一过程,那么,我们可以减少一下运算,我们可以对已经计算过的数据进行保留,这样就可以减少我们的代码量了。
代码如下:
#include <iostream>
using namespace std;
int main(){
static int N[200000];
static int p[200000][200000];
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>N[i];
}
int S=0;
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
p[i][0]=0;
p[i][j]=p[i][j-1]+N[j];
S=S>p[i][j]?S:p[i][j];
}
}
cout<<S<<endl;
return 0;
}
此时的时间复杂度是O(n2)
当然,这样还不是最优的结果
题解2+
你可以想一下,如果用树对这个进行存储,会不会更加简单一点呢?
大概时间复杂度应该是O(n*longn)
题解3
我们却发现我们还是对这个数据有重复使用,我们怎么处理呢?
思考
我们以这三个数为例,我们如果是算出2+(-4)=-2,那么,我们明显能看到,2这个数据已经使用过了,没有用了,我们是不是就可以把2抛弃
在-2和-4中选择。
这样,我们只用一个指针就可以算出了,但是,这时候,我们却发现,2如果也可以成为最优的解呢?
假如是这种情况呢?(如图)
这时,我们就想到,我们的最终目的只是找出谁最大,那么我们何不这么解决的呢?
我们用动态规划的思想,在第k位的时候,我们想,我们有两个选择,要还有不要,这样我们就可以遍历出所有的过程,当然,如果我们在过程中出现这样的情况
当我们的指针指向第1位时,我们选择了5+(-4)的解,而舍弃了-4,但是,我们却同时也没有考虑之前5的解,我们就想到了,我们可以记录每次选择的最优的解,比如,第一次指针指到5时的5,每次选择后,我们不仅继续推进,也保留当前的最优解。
#include <iostream>
using namespace std;
int main(){
static int N[200000];
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>N[i];
}
int s=N[0];
int S=s;
for(int i=1;i<n;i++){
s=N[i]+s>N[i]?N[i]+s:N[i];
S=S>s?S:s;
}
cout<<S<<endl;
return 0;
}
演示动画(这是一个动画)
最大字段和动画
课后反思
其实,我们也可以每次比较之前累加的s是不是大于0,就可以知道加上本位是不是会更优。
s=s>0?N[i]+s:N[i];
可以对这个算法进行进一步的优化。
实例
7
2 -4 3 -1 2 -4 3
注释
我们为什么使用static ,因为静态的static int 数组可以存更多的数,这样不容易爆掉。