POJ 3017 DP + 单调队列 + 堆
題意:給你一個長度為n的數列,你需要把這個數列分成幾段,每段的和不超過m,問各段的最大值之和的最小值是多少?
思路:dp方程如下:設dp[i]為把前i個數分成合法的若干段最大值的最小值是多少。dp轉移比較顯然,dp[i] = min{dp[j] + max(a[j + 1] , a[j + 2] ... + a[i])}, 其中a[j + 1] + a[j + 2] +... + a[i] <= m;這個dp轉移是O(n^2)的,我們需要用單調隊列優化。單調隊列維護的是a值單調遞減的序列(要保證與i位置的區間和小于等于m)而單調隊列的對頭不一定是最優的。需要找出單調隊列中的最小值,這個需要用堆或者線段樹來維護一下。dp[i]的轉移分為兩種,一種是j + 1 到i的和正好小于m的這種轉移,另一種是單調隊列中的最小值,兩者取min就是當前狀態的最小值。
這題有兩個點需要注意。1:若j在單調隊列里,那么max(a[j + 1] , a[j + 2] ... + a[i])是單調隊列里的下一個值。2:因為max(a[j + 1] , a[j + 2] ... + a[i])這個值是有可能隨i的變化而變化,所以,如果用堆去維護單調隊列中的值, 需要對每個j記錄一下最新的max(a[j + 1] , a[j + 2] ... + a[i]), 不能直接扔到堆里就完事了。。。或者,使用pbds中的堆,它支持對堆中元素的修改,然而POJ不支持pbds。。。。
一般堆的代碼:
#include <algorithm> #include <iostream> #include <cstdio> #include <cstring> #include <queue> #define LL long long #define pii pair<int, int> #define lowbit(x) (x << 1) #define ls(x) (x << 1) #define rs(x) ((x << 1) | 1) #define db double #define pli pair<LL, int> using namespace std; const int maxn = 100010; struct node {LL val;int pos;bool operator < (const node & rhs) const {return val > rhs.val;} }; priority_queue<node> Q; LL dp[maxn], a[maxn]; int q[maxn]; bool v[maxn]; LL val[maxn]; LL sum[maxn]; void change(LL x, int y) {Q.push((node){x, y});val[y] = x; } int main() {int n;LL m;scanf("%d%lld", &n, &m);for (int i = 1; i <= n; i++) {scanf("%lld", &a[i]);sum[i] = sum[i - 1] + a[i];}int l = 1, r = 1, ans = 0, pos = 0;dp[1] = a[1];q[1] = 1;if(a[1] > m) ans = -1;for (int i = 2; i <= n; i++) {while(sum[i] - sum[pos] > m) pos++;if(pos == i) {ans = -1;break;}while(l <= r && sum[i] - sum[q[l] - 1] > m) {v[q[l]] = 1;l++;}while(l <= r && a[q[r]] <= a[i]) {v[q[r]] = -1;r--;}if(l <= r)change(dp[q[r]] + a[i], q[r]);q[++r] = i;dp[i] = dp[pos] + a[q[l]];while(Q.size() && (v[Q.top().pos] == 1 || val[Q.top().pos] != Q.top().val)) {Q.pop();}if(Q.size()) {dp[i] = min(dp[i], Q.top().val);}}if(ans == -1) {printf("%d\n", ans);} else {printf("%lld\n", dp[n]);} }pb_ds的代碼(應該是對的吧)
#include <bits/stdc++.h> #define LL long long #define pii pair<int, int> #define lowbit(x) (x << 1) #define ls(x) (x << 1) #define rs(x) ((x << 1) | 1) #define db double #define pli pair<LL, int> #include <ext/pb_ds/priority_queue.hpp> using namespace std; using namespace __gnu_pbds; const int maxn = 100010; struct node {LL val;int pos;bool operator < (const node & rhs) const {return val > rhs.val;} }; typedef __gnu_pbds::priority_queue<node> Heap; Heap Q; Heap::point_iterator id[maxn]; LL dp[maxn], a[maxn]; int q[maxn]; bool v[maxn]; LL sum[maxn]; void change(LL x, int y) {if(id[y] != 0)Q.modify(id[y], (node){x, y});else id[y] = Q.push((node){x, y}); } int main() {int n, m;//freopen("17.in", "r", stdin);scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);sum[i] = sum[i - 1] + a[i];}int l = 1, r = 1, ans = 0, pos = 0;dp[1] = a[1];q[1] = 1;if(a[1] > m) ans = -1;for (int i = 2; i <= n; i++) {while(sum[i] - sum[pos] > m) pos++;if(pos == i) {ans = -1;break;}while(l <= r && sum[i] - sum[q[l] - 1] > m) {v[q[l]] = 1;l++;}if(l > r) {ans = -1;break;}while(l <= r && a[q[r]] <= a[i]) {v[q[r]] = 1;r--;} // Q.push(make_pair(dp[pos] + a[i], a[q[l]])); // printf("%d\n", Q.size());if(l <= r)change(dp[q[r]] + a[i], q[r]);q[++r] = i;dp[i] = dp[pos] + a[q[l]];while(Q.size() && v[Q.top().pos]) {id[Q.top().pos] = 0;Q.pop();}if(Q.size()) {//printf("%lld %d\n", Q.top().val, Q.top().pos);dp[i] = min(dp[i], Q.top().val);}}for (int i = 1; i <= n; i++)printf("%d %lld\n", i, dp[i]);if(ans == -1) {printf("%d\n", ans);} else {printf("%lld\n", dp[n]);} }
?
轉載于:https://www.cnblogs.com/pkgunboat/p/10752884.html
總結
以上是生活随笔為你收集整理的POJ 3017 DP + 单调队列 + 堆的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: @Autowired注解警告Field
- 下一篇: Linux centos ansible