算法随笔——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 式:
在其中求区间最值时,因为其上下限均单调变化,因此可以用单调队列将转移优化到
单调队列优化多重背包
思路
首先我们列出 dp 式:
我们考虑将第二维
对于每个余数
因此可以写出新的状态转移方程:
然后就可以用单调队列优化。
但我不会了,先鸽。
四边形不等式
整理自:oiwiki学习链接
四边形不等式即为:
如果对于任意
则称
决策单调性
设
定理
对于
当
分治优化
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
需要一个类似莫队的东西
线段树优化 DP
P1295 书架
P1295 [TJOI2011] 书架
一道线段树优化 DP 好题。
题意
给出一个长度为
主要思路
朴素 DP 很好列:
令
转移有:
其中
我们发现需要求区间最值,考虑线段树优化转移。
线段树中维护
想要维护这个值,需要再维护一个
考虑当前遍历到 i,会对
而
于是本题就做完了。
代码
#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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验