算法随笔——DP优化
单调队列优化DP
单调队列模板:
int head = 1,tail = 0;
for (int i = 1;i <= n;i++)
{
while (head <= tail && head 不满足条件) head++;//踢出队列
if (head <= tail) f[i] = f[q[head]] + ...;
while (head <= tail && tail 与 i 不满足单调性) tail--;
q[++tail] = i;
}
优化思路则是对于类似于这样的 dp 式:
在其中求区间最值时,因为其上下限均单调变化,因此可以用单调队列将转移优化到 \(O(1)\) 。
单调队列优化多重背包
思路
首先我们列出 dp 式:
我们考虑将第二维 \(V\) 按照除以 \(v_i\) 的余数分组。
对于每个余数 \(u \in [0,v_i-1]\),倒序循环 \(p = (V-u)/v_i\)。
因此可以写出新的状态转移方程:
然后就可以用单调队列优化。
但我不会了,先鸽。
四边形不等式
整理自:oiwiki学习链接
四边形不等式即为:
如果对于任意 \(a \le b \le c \le d\) 都有
则称 \(w\) 满足四边形不等式。
决策单调性
设 \(opt[i]\) 为 dp_i 的转移点,若对于任意 \(i < j\),都有 \(opt[i] < opt[j]\),则称该问题满足决策单调性。
定理
对于 \(f_i = \min _{1 \le j \le i} \{ f_j + w(j,i) \}\),若 \(w\) 满足四边形不等式,该问题具有决策单调性。
当 \(f\) 具有决策单调性时,我们可以使用分治优化时间至 \(O(N \log N)\)。
分治优化
void solve(int l,int r,int lopt,int ropt,int id)
{
if (l > r) return;
if (lopt > ropt) return;
int res = 0,mid = l + r >> 1;
int k = -1;
//枚举转移点
for (int i = lopt;i <= min(ropt,mid);i++)
{
int tmp = calc(i + 1,mid);
if (f[id-1][i] + tmp > res) res = f[id-1][i]+tmp,k = i;
}
f[id][mid] = res; //计算 mid的值及转移点
solve(l,mid-1,lopt,k,id); //分治
solve(mid + 1,r,k,ropt,id);
}
例题:https://www.luogu.com.cn/problem/CF833B
需要一个类似莫队的东西 \(O(1)\) 移动指针计算贡献,时间复杂度是 \(O(n \log n)\) 。
指针移动次数不超过 \(n\log n\)
线段树优化 DP
P1295 书架
P1295 [TJOI2011] 书架
一道线段树优化 DP 好题。
题意
给出一个长度为 \(n\) 的序列 \(h\),请将 \(h\) 分成若干段,满足每段数字之和都不超过 \(m\),最小化每段的最大值之和。
主要思路
朴素 DP 很好列:
令 \(f_i\) 表示考虑前 \(i\) 个数的答案。
转移有:
其中 \(g[l][r]\) 表示 \(h\) 在 \([l,r]\) 中的最大值。
我们发现需要求区间最值,考虑线段树优化转移。
线段树中维护 \([1,i-1]\) 的 $f_j + g[j+1][i] $ 的最值。
想要维护这个值,需要再维护一个 \(g[j+1][i]\),便于修改。
考虑当前遍历到 i,会对 \([premx_i,i]\) 的 \(g\) 产生贡献,将 \(g\) 变为 \(h[i]\),跑区间修改即可。
而 \(premx_i\) 可以使用 st 表提前预处理出来。
于是本题就做完了。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define int ll
#define PII pair<int,int>
int read()
{
int f=1,k=0;char c = getchar();
while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
return k*f;
}
const int N = 1e5+5;
int n,m,h[N];
int pre[N],premx[N];
int f[N];
struct node
{
int maxf,maxs,tag;
}tr[N<<2];
void pushdown(int k)
{
if (tr[k].tag)
{
tr[k<<1].maxs = tr[k<<1].maxf + tr[k].tag;
tr[k<<1|1].maxs = tr[k<<1|1].maxf + tr[k].tag;
tr[k<<1].tag = max(tr[k<<1].tag,tr[k].tag);
tr[k<<1|1].tag = max(tr[k<<1|1].tag,tr[k].tag);
tr[k].tag = 0;
}
}
void pushup(int k)
{
tr[k].maxs = min(tr[k<<1].maxs,tr[k<<1|1].maxs);
tr[k].maxf = min(tr[k<< 1].maxf,tr[k<<1|1].maxf);
}
void modifyf(int k,int l,int r,int x,int v)
{
if ( x >r || x<l) return;
if (l == r)
{
tr[k].maxs -= tr[k].maxf;
tr[k].maxf = v;
tr[k].maxs += v;
return;
}
pushdown(k);
int mid = l + r >> 1;
modifyf(k << 1,l,mid,x,v);
modifyf(k<<1|1,mid + 1,r,x,v);
pushup(k);
}
void modifyg(int k,int l,int r,int x,int y,int v)
{
if (x > r || y < l) return;
if (x <= l && r <= y)
{
tr[k].maxs = tr[k].maxf + v;
tr[k].tag = v;
return;
}
pushdown(k);
int mid = l +r >> 1;
modifyg(k<<1,l,mid,x,y,v);
modifyg(k<<1|1,mid + 1,r,x,y,v);
pushup(k);
}
void print(int k,int l,int r)
{
if (l == r)
{
cout << l<<" " << tr[k].maxf << ' ' << tr[k].maxs << endl;
return ;
}
int mid = l +r >> 1;
print(k<<1,l,mid);
print(k<<1|1,mid + 1,r);
}
int query(int k,int l,int r,int x,int y)
{
if (x > r|| y < l) return 1e18;
if (x <= l && r <= y) return tr[k].maxs;
int mid = l +r >> 1;
pushdown(k);
return min(query(k << 1,l,mid,x,y) , query(k<<1|1,mid + 1,r,x,y));
}
int dp[N][25];
void prework()
{
for (int i = 1;i <= n;i++) dp[i][0] = h[i];
for (int j = 1;j <= 20;j++)
for (int i = 1;(i + (1<<j)-1) <= n;i++)
{
dp[i][j] = max(dp[i][j-1],dp[i + (1<<(j-1))][j-1]);
}
}
int qmax(int l,int r)
{
int k = log2(r-l+1);
return max(dp[l][k],dp[r-(1<<k)+1][k]);
}
signed main()
{
cin >> n >> m;
for (int i = 1;i <= n;i++)
h[i] = read();
prework();
int sum = 0;
for (int i = n,j = n+1;i >= 1;sum-=h[i],i--)
{
while (sum+h[j-1] <= m && j > 1) sum += h[--j];
pre[i] = j;
}
for (int i = 1;i <= n;i++)
{
int l = 1,r = i;
while (l < r)
{
int mid = l +r >> 1;
if (qmax(mid,i) > h[i]) l = mid + 1;
else r = mid;
}
premx[i] = l;
}
f[0] = 0;
f[1] = h[1];
modifyf(1,1,n,1,f[1]);
for (int i = 2;i <= n;i++)
{
modifyg(1,1,n,max(premx[i]-1,1ll),i-1,h[i]);
f[i] = query(1,1,n,max(pre[i]-1,1ll),i-1ll);
if (pre[i]== 1)
{
f[i] = min(f[i],qmax(1,i));
}
modifyf(1,1,n,i,f[i]);
}
cout << f[n] << endl;
return 0;
}
斜率优化
https://vjudge.net/contest/734500#problem/J
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define int ll
#define PII pair<int,int>
#define rep(k,a,b) for (int k = a;k <= b;k++)
#define adde(a,b) v[a].push_back(b)
#define addev(a,b,c) v[a].push_back({b,c});
#define rd read
#define all(a) a.begin(),a.end()
#define mem(a,b) memset(a,b,sizeof a);
#define pb push_back
#define vct vector
#define rev(T) reverse(T.begin(),T.end())
#define endl "\n"
#define db long double
int read()
{
int f=1,k=0;char c = getchar();
while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
return k*f;
}
const int N = 2e5+5;
int n,m,p,d[N],h[N],t[N];
int sumd[N];
int tot;
int a[N];
int cnt[N];
int f[N][105];
int q[N],tt,hh,S[N];
int nowp;
int X(int x){return x;}
int Y(int x){return f[x][nowp-1] +S[x]; }
int K(int x){return a[x];}
db slope(int a,int b)
{
if (X(a) == X(b)) return (Y(a)>=Y(b))?INF:-INF;
return (Y(a)-Y(b))/(X(a)-X(b));
}
void solvemain()
{
cin >> n >> m >> p;
rep(i,1,n-1) cin >> d[i],sumd[i+1] = sumd[i] + d[i];
rep(i,1,m) cin >> h[i] >> t[i],t[i] -= sumd[h[i]];
sort(t + 1,t + m + 1);
n = m;
for (int i = 1;i <= n;i++) a[i] = t[i],S[i] = S[i-1] + a[i];
memset(f,INF,sizeof f);
f[0][0] = 0;
// for (int i = 1;i <= n;i++) cout << a[i] << ' ';puts("");
for (int k =1;k <= p;k++)
{
nowp = k;
hh = 1,tt = 0;
f[0][k] = 0;
q[++tt] = 0; //记得将 push 0
for (int i = 1;i <= n;i++)
{
while (hh < tt && slope(q[hh],q[hh+1]) <= K(i)) hh++;
f[i][k] = Y(q[hh]) - K(i) * X(q[hh]) - S[i] + a[i]*i;
while (hh < tt && slope(q[tt-1],q[tt]) >= slope(q[tt],i)) tt--;
q[++tt] = i;
}
}
cout << f[n][p] << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;t = 1;
while(t--)
{
solvemain();
}
return 0;
}

浙公网安备 33010602011771号