近期打算整理一下DP的优化部分, 主要参照HH的博客顺序HH关于DP优化进行训练,本篇将持续更新。
单调队列优化DP
斜率优化DP
平行四边形优化
滚动数组优化(空间优化)
数据结构优化DP(线段树等)
单调队列:
单调队列优化DP:
题意:
分析:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxab(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 2010;
const int INF = 0x3f3f3f3f;
int f[N][N];
int qu[N], pos[N];
int main(){
int T;
int n, maxp, w;
int Ap, Bp, As, Bs;
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &maxp, &w);
for(int i = 1; i <= w + 1; ++i){
scanf("%d%d%d%d", &Ap, &Bp, &As, &Bs);
for(int j = 0; j <= maxp; ++j){
if(j <= As) f[i][j] = -Ap*j;
else f[i][j] = -INF;
if(i > 1 && f[i - 1][j] > f[i][j])
f[i][j] = f[i - 1][j];
}
}
for(int i = w + 2; i <= n; ++i){
scanf("%d%d%d%d", &Ap, &Bp, &As, &Bs);
int p = 0, q = -1;
int k = i - w - 1;
for(int j = 0; j <= maxp; ++j){
int tmp = f[k][j] + j*Ap;
while(p <= q && qu[q] < tmp){
--q;
}
qu[++q] = tmp;
pos[q] = j;
while(j - pos[p] > As) ++p;
f[i][j] = maxab(f[i - 1][j], qu[p] - j*Ap);
}
p = 0, q = -1;
for(int j = maxp; j >= 0; --j){
int tmp = f[k][j] + j*Bp;
while(p <= q && qu[q] < tmp){
--q;
}
qu[++q] = tmp;
pos[q] = j;
while(pos[p] - j > Bs) ++p;
f[i][j] = maxab(f[i][j], qu[p] - j*Bp);
}
}
printf("%d\n", f[n][0]);
}
return 0;
}
题意:这道题目很有趣, 以电影《海上钢琴师》的1900作为题目背景。大概的意思是:在m*n的区域内, 有些地方能碰, 有些地方不能碰。有k个时间段, 每个时间段内, 可能会做出上下左右四个方向的移动。给出初始位置, 以及K各时间段的信息, 求出最大能够移动的距离。
分析:
1. 首先确定是DP,设f[k][x][y]表示:第K个时间区间结束之后,在[x, y]位置能够移动的最大距离。
2. f[k][x][y] = max(f[k-1][x1][y1] + dist) 比如k个区间是做向上的操作, 那么f[k][x][y] = max(f[k-1][x1][y] + (x1 - x)) (x1 - x <= T) T表示时间段[s,e]的长度 T=e-s+1; 时间复杂度最大可到O(k*max(m*n^2, n*m^2)), 初始状态为f[0][x][y] = 0。
3. 跟上题一样, 我们可以考虑对f[k-1][x1][y1] + x1这样的状态进行单调队列的优化。具体方法与上题一模一样。 这样就可以优化掉决策里面的一维, 最大的时间复杂度:O(k*mn); 同时注意用滚动数组对空间进行优化。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 210;
const int INF = 0x3f3f3f3f;
char s[N][N];
int f[2][N][N];
int dq[N], pos[N];
int fx[4] = {-1, 1, 0, 0};
int fy[4] = {0, 0, -1, 1};
int m, n;
void gao(int k, int x, int y, int size, int w, int d){
int p = 0, q = -1;
for(int i = 1; i <= w; ++i){
if(s[x][y] == '.'){
int tmp = f[(k-1) & 1][x][y] - i;
while(p <= q && dq[q] < tmp) --q;
dq[++q] = tmp, pos[q] = i;
while(i - pos[p] > size){
++p;
}
f[k & 1][x][y] = dq[p] + i;
}
else{
p = 0, q = -1;
f[k & 1][x][y] = -INF;
}
x += fx[d], y += fy[d];
}
}
int main(){
int x, y, k, d;
while(~scanf("%d %d", &m, &n)){
scanf("%d %d %d", &x, &y, &k);
for(int i = 0; i < m; ++i){
scanf("%s", s[i]);
}
for(int i = 0; i < m; ++i)
for(int j = 0; j < n; ++j){
f[0][i][j] = -INF;
}
f[0][x - 1][y - 1] = 0;
for(int i = 1; i <= k; ++i){
scanf("%d %d %d", &x, &y, &d);
if(d == 1){
for(int j = 0; j < n; ++j){
gao(i, m - 1, j, y - x + 1, m, 0);
}
}
else if(d == 2){
for(int j = 0; j < n; ++j){
gao(i, 0, j, y - x + 1, m, 1);
}
}
else if(d == 3){
for(int j = 0; j < m; ++j){
gao(i, j, n - 1, y - x + 1, n, 2);
}
}
else{
for(int j = 0; j < m; ++j){
gao(i, j, 0, y - x + 1, n, 3);
}
}
}
int ans = 0;
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
if(f[k&1][i][j] > ans) ans = f[k&1][i][j];
}
}
printf("%d\n", ans);
}
return 0;
}