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
例如 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个数找到最大子序和
第二行有n个数,要求在n个数找到最大子序和
输出格式
一个数,数出他们的最大子序和
测试样例1
输入
6 4
1 -3 5 1 -2 3
输出
7
备注
数据范围:
100%满足n,m<=300000
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\n",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\n",ans); 19 return 0; 20 }
相同的地方是两个题都是单调队列优化+DP