P1115 最大子段和

链接:https://www.luogu.com.cn/problem/P1115

方法一:0分代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, a[210000];
 4 int ans=-(1<<30);
 5 int sum(int l, int r){
 6     int ret=0;
 7     for(int i=l; i<=r; i++)ret+=a[i];
 8     return ret;
 9 }
10 int main()
11 {
12     cin>>n;
13     for(int i=1; i<=n; i++)cin>>a[i]; 
14     for(int i=1; i<=n; i++)
15         for(int j=i; j<=n; j++)
16             ans=max(ans,sum(i, j));
17     cout<<ans;    
18     return 0;    
19 } 

 

方法二:使用前缀和,但无优化(40分代码)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, a[210000], presum[210000];
 4 int ans=-(1<<30);//答案求最大值,初始化为最小值 
 5 int main()
 6 {
 7     cin>>n;
 8     for(int i=1; i<=n; i++)cin>>a[i];
 9     for(int i=1; i<=n; i++)presum[i]=presum[i-1]+a[i];//求前缀和 
10     for(int i=1; i<=n; i++)
11         for(int j=i; j<=n; j++)
12             ans=max(ans,presum[j]-presum[i-1]);
13     cout<<ans;    
14     return 0;    
15 } 

方法三:前缀和加优化(100分)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, a[210000], psum[210000];
 4 int minl;//用于存放左端点最小的前缀和值 
 5 int ans=-(1<<30);
 6 int main()
 7 {
 8     cin>>n;
 9     for(int i=1; i<=n; i++){
10         cin>>a[i];
11         psum[i]=psum[i-1]+a[i];//输入同时求出前缀和 
12     }
13     minl=0;
14     for(int i=1; i<=n; i++){
15         ans=max(ans, psum[i]-minl);//求最大的区间和 
16         minl=min(minl, psum[i]);//依次枚举最小左端点 
17     }
18     cout<<ans;
19     return 0;
20 }

方法三:分治

你应该听说过分治法,正是:分而治之。我们有一个很复杂的大问题,很难直接解决它,但是我们发现可以把问题划分成子问题,如果子问题规模还是太大,并且它还可以继续划分,那就继续划分下去。直到这些子问题的规模已经很容易解决了,那么就把所有的子问题都解决,最后把所有的子问题合并,我们就得到复杂大问题的答案了。可能说起来简单,但是仍不知道怎么做,接下来分析这个问题:
首先,我们可以把整个序列平均分成左右两部分,答案则会在以下三种情况中:
1、所求序列完全包含在左半部分的序列中。
2、所求序列完全包含在右半部分的序列中。
3、所求序列刚好横跨分割点,即左右序列各占一部分。
前两种情况和大问题一样,只是规模小了些,如果三个子问题都能解决,那么答案就是三个结果的最大值。我们主要研究一下第三种情况如何解决:

我们只要计算出:以分割点为起点向左的最大连续序列和、以分割点为起点向右的最大连续序列和,这两个结果的和就是第三种情况的答案。因为已知起点,所以这两个结果都能在O(N)的时间复杂度能算出来。

递归不断减小问题的规模,直到序列长度为1的时候,那答案就是序列中那个数字。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 //N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
 5 int N, num[16777216];
 6 
 7 int solve(int left, int right)
 8 {
 9     //序列长度为1时
10     if(left == right)
11         return num[left];
12     
13     //划分为两个规模更小的问题
14     int mid = left + right >> 1;
15     int lans = solve(left, mid);
16     int rans = solve(mid + 1, right);
17     
18     //横跨分割点的情况
19     int sum = 0, lmax = num[mid], rmax = num[mid + 1];
20     for(int i = mid; i >= left; i--) {
21         sum += num[i];
22         if(sum > lmax) lmax = sum;
23     }
24     sum = 0;
25     for(int i = mid + 1; i <= right; i++) {
26         sum += num[i];
27         if(sum > rmax) rmax = sum;
28     }
29 
30     //答案是三种情况的最大值
31     int ans = lmax + rmax;
32     if(lans > ans) ans = lans;
33     if(rans > ans) ans = rans;
34 
35     return ans;
36 }
37 
38 int main()
39 {
40     //输入数据
41     scanf("%d", &N);
42     for(int i = 1; i <= N; i++)
43         scanf("%d", &num[i]);
44 
45     printf("%d\n", solve(1, N));
46 
47     return 0;
48 }

 

方法五:动态规划

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int k;
 4 int a[200005];
 5 int dp[200005];//当前位置之前的最大字段和 
 6 int main()
 7 {
 8     scanf("%d",&k);
 9     for(int i=0; i<k; i++)
10         scanf("%d",&a[i]);
11     dp[0]=a[0];
12     int ans=dp[0];
13     for(int i=1; i<k; i++)
14     {
15         dp[i]=max(a[i], dp[i-1]+a[i]);//dp[i]与a[i]有关 
16         ans=max(ans, dp[i]);
17     }
18     //(int i=0; i<k; i++)printf("%d ",dp[i]);
19     //printf("\n");
20     printf("%d",ans);
21     return 0;
22 }

 

posted @ 2020-10-15 16:25  TFLSNOI  阅读(160)  评论(0编辑  收藏  举报