【整理】斜率or单调队列优化dp
2015-03-18 21:16
441 查看
【1】HDU2993 MAX Average Problem
题意:求一个序列的子区间满足长度大于k且所有数平均值最大周源论文里的题。。之前有人说周源讲的是错的 其实应该是没什么问题的 可以O(n)求出
不过这题hdu上的数据不知道怎么了 = = 反正我在网上找的ac代码们全都TLE。。。。
总之……意思明白就好 反正也是入门题……
#include <cstdio> #include <iostream> #include <cstdlib> #include <cmath> using namespace std; int read() { int sign = 1, n = 0; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); } while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); } return sign*n; } int N, K; int a[100005]; double sum[100005]; namespace queue{ int q[100005], head, tail; inline void init(){ head = 1; tail = 0; } inline void push_back(int x){ q[++tail] = x; } inline void pop_back() { --tail; } inline void pop_front() { ++head; } inline int front() { return q[head]; } inline int second() { return q[head + 1]; } inline int back() { return q[tail]; } inline int before() { return q[tail - 1]; } inline int size() { return tail - head + 1; } } inline double get_k(int i, int j){ return (sum[i] - sum[j]) / (i - j); } int main() { while(~scanf("%d%d", &N, &K)) { for(int i = 1; i <= N; ++i) { a[i] = read(); sum[i] = sum[i - 1] + a[i]; } using namespace queue; double ans = 1e-100; init(); for(int i = K; i <= N; ++i) { int now = i - K; while (size() > 1 && get_k(back(), before()) >= get_k(now, back()) ) pop_back(); push_back(now); while (size() > 1 && get_k(front(), i) <= get_k(second(), i)) pop_front(); ans = fmax(ans, get_k(front(), i)); } printf("%.2f\n", ans); } return 0; }
【2】HDU3480 Division
题意:把一个集合 分成若干个子集合 每个集合的cost为最大值与最小值差的平方 求最大cost的最小值和之前摩天轮那道题是一样的。。先排个序 很明显每个集合是要取连续的一段区间
那么dp[i][j]表示前i个人分成了j个区间的最大cost的最小值。。于是dp[i][j] = max{ dp[k-1][j-1] + (sum[i] - sum[k]) ^ 2 }
然后展开以后就可以斜率优化了……还有就是第二维可以滚动。。
说实话这题写的我略痛苦。。因为还是不太熟TAT
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; int read() { int sign = 1, n = 0; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); } while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); } return sign*n; } const int Nmax = 10005; int N, M; int a[Nmax], sqr[Nmax]; int dp[Nmax][2], G[Nmax]; namespace queue{ int q[100005], head, tail; inline void init(){ head = 1; tail = 0; } inline void push_back(int x){ q[++tail] = x; } inline void pop_back() { --tail; } inline void pop_front() { ++head; } inline int front() { return q[head]; } inline int second() { return q[head + 1]; } inline int back() { return q[tail]; } inline int before() { return q[tail - 1]; } inline int size() { return tail - head + 1; } } inline double getk(int i, int j) { if(a[i] == a[j]) return 1e200; return 1. * (G[i] - G[j]) / (a[i] - a[j]); } int main() { for(int T = read(), cas = 0; T--; ) { N = read(), M = read(); for(int i = 1; i <= N; ++i) { a[i] = read(); dp[i][0] = 0x3f3f3f3f; } sort(a + 1, a + N + 1); for(int i = 1; i <= N; ++i) sqr[i] = a[i] * a[i]; using namespace queue; for(int j = 1; j <= M; ++j) { int nw = j & 1, pr = nw ^ 1; init(); for(int i = j; i <= N; ++i) { G[i] = dp[i - 1][pr] + sqr[i]; while(size() > 1 && getk(before(), back()) >= getk(back(), i)) pop_back(); push_back(i); while(size() > 1 && getk(front(), second()) <= 2. * a[i]) pop_front(); dp[i][nw] = G[front()] + sqr[i] - 2 * a[i] * a[front()]; } } printf("Case %d: %d\n", ++cas, dp [M & 1]); } return 0; }
【3】HDU3530 Subsequence
题意:找出一个数列中的最长的一个子区间 满足最大值和最小值之差在[M, K]之间开两个单调队列一个维护递增一个维护递减就可以了。。然后每次判断队首合不合法。。。并更新答案。。。
这题我最开始队列初始化为a[1]就一直会挂 = =我也不知道为什么
#include <cstdio> #include <iostream> #include <cstdlib> using namespace std; int read() { int sign = 1, n = 0; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); } while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); } return sign*n; } int N, M, K; int a[100005]; struct queue{ int Q[100005], head, tail; inline void init(){ head = 1; tail = 0; } inline void push_back(int x){ Q[++tail] = x; } inline void pop_back() { --tail; } inline void pop_front() { ++head; } inline int front() { return Q[head]; } inline int back() { return Q[tail]; } inline int size() { return tail - head + 1; } }q[2]; int main() { while(~scanf("%d%d%d", &N, &M, &K)) { for(int i = 1; i <= N; ++i) a[i] = read(); int left = 0, ans = 0; q[0].init(); q[1].init(); for(int i = 1; i <= N; ++i) { while(q[0].size() && a[q[0].back()] <= a[i]) q[0].pop_back(); q[0].push_back(i); while(q[1].size() && a[q[1].back()] >= a[i]) q[1].pop_back(); q[1].push_back(i); while(a[q[0].front()] - a[q[1].front()] > K) { if(q[0].front() < q[1].front()){ left = q[0].front(); q[0].pop_front(); } else { left = q[1].front(); q[1].pop_front(); } } if(a[q[0].front()] - a[q[1].front()] >= M) ans = max(ans, i - left); } printf("%d\n", ans); } return 0; }
【4】HYSBZ1911 特别行动队
题意:……中文题就不写了……然后这题的公式还是蛮好推的。。但是因为特别长所以要注意细节。。。
A是小于0的 我们要求的是最大值 那么就维护一个上凸包(其实也可以转化成下凸包。。都一样)
f[i] = max( f[j] + a*(s[i]-s[j])^2 + b*(s[i]-s[j]) + c)
= ( f[j] + a*s[j]^2 - b*s[j] ) + ( a*s[i]^2 +b*s[i]+c ) - ( 2*a*s[i]*s[j] )
分三块。。很明显是不是。。
#include <cstdio> #include <iostream> using namespace std; typedef long long LL; const int Nmax = 1000005; int N; LL a[Nmax], sum[Nmax], sqr[Nmax], A, B, C; LL dp[Nmax], F[Nmax]; namespace queue{ int q[Nmax], head, tail; inline void init(){ head = 1; tail = 0; } inline void push_back(int x){ q[++tail] = x; } inline void pop_back() { --tail; } inline void pop_front() { ++head; } inline int front() { return q[head]; } inline int second() { return q[head + 1]; } inline int back() { return q[tail]; } inline int before() { return q[tail - 1]; } inline int size() { return tail - head + 1; } } double get_k(int i, int j) { if(sum[i] == sum[j]) return 1e200; return 1. * (F[i] - F[j]) / (sum[i] - sum[j]); } int main() { ios::sync_with_stdio(false); cin >> N >> A >> B >> C; for(int i = 1; i <= N; ++i) { cin >> a[i]; sum[i] = sum[i - 1] + a[i]; sqr[i] = sum[i] * sum[i]; dp[i] = A * sqr[i] + B * sum[i] + C; } using namespace queue; init(); for(int i = 1; i <= N; ++i) { int K = 2 * A * sum[i]; while(size() > 1 && get_k(front(), second()) >= K) pop_front(); dp[i] = max(dp[i], F[front()] + dp[i] - 2 * A * sum[front()] * sum[i]); F[i] = dp[i] + A * sqr[i] - B * sum[i]; while(size() > 1 && get_k(before(), back()) <= get_k(back(), i)) pop_back(); push_back(i); } cout << dp << endl; return 0; }
【5】HDU 3507 Print Article
题意:把N个字母分成若干行 每个字母有个ci 然后每行的cost为sigma(ci) + M 求总cost的最小值和上面那题差不多 而且公式还简单些 = =
#include <cstdio> #include <iostream> using namespace std; typedef long long LL; const int Nmax = 500005; int N, M; LL a[Nmax], sum[Nmax], sqr[Nmax]; LL dp[Nmax], F[Nmax]; namespace queue{ int q[Nmax], head, tail; inline void init(){ head = 1; tail = 0; q[head] = 0;} inline void push_back(int x){ q[++tail] = x; } inline void pop_back() { --tail; } inline void pop_front() { ++head; } inline int front() { return q[head]; } inline int second() { return q[head + 1]; } inline int back() { return q[tail]; } inline int before() { return q[tail - 1]; } inline int size() { return tail - head + 1; } } double get_k(int i, int j) { if(sum[i] == sum[j]) return 1e200; return 1. * (F[i] - F[j]) / (sum[i] - sum[j]); } int main() { ios::sync_with_stdio(false); while(cin >> N >> M) { for(int i = 1; i <= N; ++i) { cin >> a[i]; sum[i] = sum[i - 1] + a[i]; sqr[i] = sum[i] * sum[i]; dp[i] = sqr[i] + M; } using namespace queue; init(); for(int i = 1; i <= N; ++i) { int K = 2 * sum[i]; while(size() > 1 && get_k(front(), second()) <= K) pop_front(); dp[i] = min(dp[i], F[front()] + dp[i] - 2 * sum[front()] * sum[i]); F[i] = dp[i] + sqr[i]; while(size() > 1 && get_k(before(), back()) >= get_k(back(), i)) pop_back(); push_back(i); } cout << dp << endl; } return 0; }
【6】HDU 3669 Cross the Wall
题解:http://blog.csdn.net/qq_21841245/article/details/44461527相关文章推荐
- 关于DP的单调队列优化和斜率优化区别
- 模型化理解单调队列优化和斜率优化DP
- 斜率优化第一题! HDU3507 | 单调队列优化DP
- [置顶] 【专题】单调队列/斜率优化DP
- 单调队列 && 斜率优化dp 专题
- bzoj1597: [Usaco2008 Mar]土地购买(斜率优化+Dp+单调队列)
- 【专题】单调队列/斜率优化DP
- 【DP】【斜率优化】【单调队列优化】Gift 题解
- 【GDOI2014模拟】服务器 (斜率单调队列优化Dp)
- CF185 div1 B. Cats Transport(单调队列斜率优化dp)
- dp部分总结(单调队列,四边形优化,斜率优化,树形dp)
- 【专题】单调队列/斜率优化DP
- DP的各种优化(动态规划,决策单调性,斜率优化,带权二分,单调栈,单调队列)
- BZOJ.1010.[HNOI2008]玩具装箱toy(DP 斜率优化/单调队列 决策单调性)
- 斜率优化+单调队列优化DP
- 单调队列+斜率优化的DP
- dp的斜率优化与单调队列优化
- 单调队列,斜率优化dp 专题
- hdu3507 Print Article 单调队列斜率优化DP
- 【专辑】单调队列+斜率优化的DP