題目描述
給定一行n個非負整數a[1]..a[n]。如今你能夠選擇其中若干個數,但不能有超過k個連續的數字被選擇。你的任務是使得選出的數字的和最大。ios
錯誤日誌: longlong 的 \(inf\) 沒有設爲 0xfffffffffffffff優化
正難則反
正難則反
複習看到了雙倍經驗就回來看看
求最大值有點難, 轉化爲求最小去除值
設 \(dp[i]\) 爲 \(i\) 爲斷點, 考慮到 \(i\) 處的最小去除值
發現每隔 \(K + 1\) 必有一個斷點
因此 \(dp[i] = \min_{j = i - k - 1}^{i - 1}dp[j] + a[i]\)
而後單調隊列優化, 答案在區間 \([n - K, n]\) 內
總值減一下便可spa
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #include<algorithm> #include<climits> #define LL long long #define REP(i, x, y) for(LL i = (x);i <= (y);i++) using namespace std; LL RD(){ LL out = 0,flag = 1;char c = getchar(); while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();} while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();} return flag * out; } const LL maxn = 200019, inf = 0xfffffffffffffff; LL num, K; LL a[maxn], sum; LL dp[maxn]; struct Que{ LL Index, val; }Q[maxn]; LL head = 1, tail; void push_back(LL Index, LL val){ while(head <= tail && Q[tail].val >= val)tail--; Q[++tail] = (Que){Index, val}; } LL get_min(LL p){ while(head <= tail && p - Q[head].Index > K + 1)head++; return Q[head].val; } void init(){ num = RD(), K = RD(); REP(i, 1, num)a[i] = RD(), sum += a[i]; memset(dp, 127, sizeof(dp)); push_back(0, 0); } void solve(){ REP(i, 1, num){ dp[i] = get_min(i) + a[i]; push_back(i, dp[i]); } LL ans = inf; REP(i, num - K, num)ans = min(ans, dp[i]); printf("%lld\n", sum - ans); return ; REP(i, 1, num)printf("%lld ", dp[i]); } int main(){ init(); solve(); return 0; }