TYVJ 1305 最大子序和 ++ 烽火传递

描述

输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。

例如 1,-3,5,1,-2,3

当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6

输入格式

第一行两个数n,m
第二行有n个数,要求在n个数找到最大子序和

输出格式

一个数,数出他们的最大子序和

测试样例1

输入

6 4 
1 -3 5 1 -2 3

输出

7

备注

数据范围:
100%满足n,m<=300000
 
 
用单调队列维护从开始到[i]范围内的最小值,总和减去该最小值,就是最大序列和。
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n,m,f[300010],p[300010],sum=0,tail,head,x,ans=0;
 5 int main()
 6 {
 7     scanf("%d%d",&n,&m);
 8     head=1,tail=2;
 9     for(int i=1;i<=n;i++){
10         scanf("%d",&x);sum+=x;
11         while(f[tail-1]>=sum && tail>head) tail--;
12         f[tail]=sum;p[tail]=i;
13         tail++;
14         while(i-p[head]>m) head++;
15         if(sum-f[head]>ans) ans=sum-f[head];
16     }
17     printf("%d
",ans);
18     return 0;
19 }

烽火传递:

给定n个非负整数,选择其中若干数字,使得每连续k个数中至少有一个数被选出。 要求选择的数字之和尽可能小。

据说是NOIP考试题,但是我没有在OnlineJudge上找到,所以也没有测评

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 1000010
 4 using namespace std;
 5 int l=0,r=0,n,m,a[maxn],q[maxn*2],f[maxn];
 6 int main()
 7 {
 8     scanf("%d%d",&n,&m);
 9     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
10     for(int i=1;i<=n;i++){
11         while(l<r&&i-q[l]>m) l++;
12         f[i]=f[q[l]]+a[i];
13         while(l<r&&f[q[r]]>f[i]) r--;
14         q[++r]=i;
15     }
16     int ans=0x7f;
17     for(int i=n-m+1;i<=n;i++) ans=min(ans,f[i]);
18     printf("%d
",ans);
19     return 0;
20 }

相同的地方是两个题都是单调队列优化+DP

原文地址:https://www.cnblogs.com/suishiguang/p/6361632.html