P2627 修剪草坪[dp][單調隊列]
阿新 • • 發佈:2019-02-15
lin clas def time back for its 數組 long long
P2627 修剪草坪
給你一個\(n\)個數字的數組,至多連續取\(k\)個數字,求取出的最大和。
預處理了前綴和之後,一維dp很容易想:\(dp[i] = max(dp[j-1] + sum[j+1,i])\)
用前綴和寫就是\(dp[i]=max(dp[j-1]+sum[i]-sum[j])\)
把與\(i\)有關的拿出\(max\),就有\(dp[i]=sum[i]+max(dp[j-1]-sum[j])\)
\(j\)能取的是一個固定區間,長度為\(k\),所以直接單調隊列維護咯!
維護一個單調遞減的隊列,隊頭就是最大值了。
復雜度為\(O(n)\)。太優美了!
代碼:
/************************************************************************* @Author: Garen @Created Time : Tue 12 Feb 2019 10:19:25 AM CST @File Name: P2627.cpp @Description: ************************************************************************/ #include<bits/stdc++.h> #define ll long long const ll maxn = 100005; ll dp[maxn]; ll a[maxn], sum[maxn]; ll n, k; std::deque<ll> q; ll d[maxn]; ll update(ll i) { d[i] = dp[i - 1] - sum[i]; while(!q.empty() && d[q.back()] < d[i]) q.pop_back(); q.push_back(i); while(!q.empty() && q.front() < i - k) q.pop_front(); return d[q.front()]; } int main() { scanf("%lld %lld", &n, &k); for(ll i = 1; i <= n; i++) { scanf("%lld", &a[i]); sum[i] = sum[i - 1] + a[i]; } /* for(ll i = 1; i <= k; i++) dp[i] = sum[i]; for(ll i = k + 1; i <= n; i++) { for(ll j = i - k; j <= i; j++) { dp[i] = std::max(dp[i], dp[j - 1] + sum[i] - sum[j]); } // dp[i] = sum[i] + std::max(dp[j - 1] - sum[j])); } */ q.push_back(0); for(ll i = 1; i <= n; i++) { dp[i] = sum[i] + update(i); } printf("%lld\n", dp[n]); return 0; }
P2627 修剪草坪[dp][單調隊列]