这题是有下界的最大子段和, 无上下界的最大子段和请看
hh大牛把这个归为单纯的单调队列题, 因为这个状态间不用转移, 其实无所谓啦, 思路都是一样的
思路:
单调队列优化dp
以i结尾的最大子段和 d[i] = max{ sum[i]-sum[k] | k=[i-K , i-1] }.
化为 d[i] = max(f[k])+sum[i]. f[k]=-sum[k], k=[i-k, i-1]. k>=0
另外, 题目是环, 把序列处理成2*n就行了.
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<cmath> #include<string> #include<vector> #include<map> #include<algorithm> using namespace std; inline int Rint() { int x; scanf("%d", &x); return x; } inline int max(int x, int y) { return (x>y)? x: y; } inline int min(int x, int y) { return (x<y)? x: y; } #define FOR(i, a, b) for(int i=(a); i<=(b); i++) #define FORD(i,a,b) for(int i=(a);i>=(b);i--) #define REP(x) for(int i=0; i<(x); i++) typedef long long int64; #define INF (1<<30) const double eps = 1e-8; #define bug(s) cout<<#s<<"="<<s<<" " // 单调队列优化dp // 以i结尾的最大子段和 d[i] = max{ sum[i]-sum[k] | k=[i-K , i-1] }. // 化为 d[i] = max(f[k])+sum[i]. f[k]=-sum[k], k=[i-k, i-1]. k>=0 // 另外, 题目是环, 把序列处理成2*n就行了. #define MAXN 100002*2 //环 int sum[MAXN]; int f[MAXN]; int q[MAXN]; int front, tail; int main() { int T = Rint(); while(T--) { sum[0] = 0; int n = Rint(); int K = Rint(); FOR(i, 1, n) { int t = Rint(); sum[i]=sum[i-1]+t; sum[i+n] = sum[i]; } FOR(i, 1+n, n+n) { sum[i] += sum[n]; } front = tail = 0; f[0] = 0; int maxd = -INF; int st=1, en=1; FOR(i, 1, n*2) { f[i] = -sum[i]; // 把i-1丢进队列 while(front<tail && f[q[tail-1]]<f[i-1]) tail--; q[tail++] = i-1; // 算d[i] int low = max(i-K, 0); while(q[front]<low) front++; int d = f[q[front]]+sum[i]; if(d>maxd) { maxd = d; st = q[front]+1; //这里出现的st , 必小于 n en = i>n? i-n: i; } } printf("%d %d %d\n", maxd, st, en); } }