DP的优化 1
1.0单调队列
在计算形如 ,那么时间复杂度就会大大降低,
显然在中,只有最大值有用,所以我们就可以利用单调队列,维护区间最大值,进行求解.
例题:
I P1725 琪露诺
单调队列板子题
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,l,r;
int w[maxn*2];
int dp[maxn*2],ans=-0x3f3f3f3f;
struct node{
int p,a;
}q[maxn];
int rd(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int main(){
// freopen("A.in","r",stdin);
// freopen("A.out","w",stdout);
n=rd();l=rd();r=rd();
for(int i=0;i<=n;i++) w[i]=rd();
int l1=0,r1=0;
for(int i=1;i<=n+r;i++) dp[i]=-0x3f3f3f3f;
dp[0]=0;
for(int i=l;i<=n+r;i++){
while(l1<=r1&&q[r1].p>i-l) r1--;
while(l1<=r1&&q[l1].p<i-r) l1++;
dp[i]=q[l1].a+w[i];
while(l1<=r1&&dp[i-l+1]>q[r1].a) r1--;
q[++r1].a=dp[i-l+1];
q[r1].p=i-l+1;
}
for(int i=n+1;i<=n+r;i++) ans=max(ans,dp[i]);
cout<<ans;
return 0;
}
II P2254 [NOI2005] 瑰丽华尔兹
单调队列进阶版,需要进行一定的转化.(好久之前做的,以后来补题解)
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e2+5;
int n,m,x,y,k;
char mapp[maxn][maxn];
int dp[maxn][maxn][maxn];
struct node{
int q1,v;
}q[maxn];
int main(){
cin>>n>>m>>x>>y>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mapp[i][j];
for(int k1=0;k1<=k;k1++)
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
dp[k1][i][j]=-0x3f3f3f3f;
dp[0][x][y]=dp[1][x][y]=0;
for(int k1=1;k1<=k;k1++){
int s,t,d;
int l=1,r=0;
cin>>s>>t>>d;
int len=t-s+1;
if(d==1){
for(int j=1;j<=m;j++){
l=1;r=0;
int f=1;
for(int i=n;i>=1;i--){
if(mapp[i][j]=='x'){l=1,r=0;continue;}
// if(dp[k1-1][i][j]>0&&f==1)
// dp[k1][i][j]=max(dp[k1][i][j],dp[k1-1][i][j]),f=0;
while(l<=r&&q[l].q1>i+len) l++;
while(dp[k1-1][q[r].q1][j]+q[r].q1<dp[k1-1][i][j]+i&&l<=r) r--;
q[++r].q1=i;
q[r].v=dp[k1-1][i][j];
dp[k1][i][j]=max(dp[k1-1][i][j],q[l].v+q[l].q1-i);
// if(j==3) cout<<dp[k1][i][j]<<endl;
}
}
}
if(d==2){
for(int j=1;j<=m;j++){
l=1,r=0;
int f=1;
for(int i=1;i<=n;i++){
if(mapp[i][j]=='x'){l=1,r=0;continue;}
// if(dp[k1-1][i][j]>0&&f==1)
// dp[k1][i][j]=max(dp[k1][i][j],dp[k1-1][i][j]),f=0;
while(l<=r&&q[l].q1<i-len) l++;
while(dp[k1-1][q[r].q1][j]-q[r].q1<dp[k1-1][i][j]-i&&l<=r) r--;
q[++r].q1=i;
q[r].v=dp[k1-1][i][j];
dp[k1][i][j]=max(dp[k1-1][i][j],q[l].v+i-q[l].q1);
}
}
}
if(d==3){
for(int i=1;i<=n;i++){
l=1,r=0;
int f=1;
for(int j=m;j>=1;j--){
if(mapp[i][j]=='x'){l=1,r=0;continue;}
// if(dp[k1-1][i][j]>0&&f==1)
// {dp[k1][i][j]=max(dp[k1][i][j],dp[k1-1][i][j]);f=0;}
while(l<=r&&q[l].q1>j+len) l++;
while(dp[k1-1][i][q[r].q1]+q[r].q1<dp[k1-1][i][j]+j&&l<=r) r--;
q[++r].q1=j;
q[r].v=dp[k1-1][i][j];
dp[k1][i][j]=max(dp[k1-1][i][j],q[l].v+q[l].q1-j);
// if(i==2) cout<<dp[k1][i][j]<<endl;
}
}
}
if(d==4){
for(int i=1;i<=n;i++){
l=1,r=0;
int f=1;
for(int j=1;j<=m;j++){
if(mapp[i][j]=='x'){l=1,r=0;continue;}
// if(dp[k1-1][i][j]>0&&f==1)
// dp[k1][i][j]=max(dp[k1][i][j],dp[k1-1][i][j]),f=0;
while(l<=r&&q[l].q1<j-len) l++;
while(dp[k1-1][i][q[r].q1]-q[r].q1<dp[k1-1][i][j]-j&&l<=r) r--;
q[++r].q1=j;
q[r].v=dp[k1-1][i][j];
dp[k1][i][j]=max(dp[k1-1][i][j],q[l].v+j-q[l].q1);
// if(i==4) cout<<dp[k1][i][j]<<endl;
}
}
}
}
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,dp[k][i][j]);
cout<<ans;
return 0;
}
IIIPOJ1821 Fence
先将s排序,便于按照顺序进行dp。定义为前个人涂前块木板的最大答案.
考虑当前这个人涂或者不涂,是否涂第块木板容易写出
()
(分离决策变量k和状态变量j)
注意到的作用就是枚举求出对应的区间最值,于是有
当我们转移,循环到时,注意到每加一位,区间也会随之向右线性变化.
只需利用单调队列预先处理出这个包含的区间最值.再动态维护合法区间即可.
一道简单题还调了好久
代码:
点击查看代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e2+10;
int n,k,p[maxn],s[maxn],l[maxn];
int dp[maxn][16010];
struct node{
int p1,value;
}q[16010];
struct node1{
int l,s,p;
}e[maxn];
int rd(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
char ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
bool cmp(node1 x,node1 y){
return x.s<y.s;
}
int main(){
n=rd();k=rd();
for(int i=1;i<=k;i++){
e[i].l=rd();
e[i].p=rd();
e[i].s=rd();
}
sort(e+1,e+k+1,cmp);
for(int i=1;i<=k;i++)
for(int j=1;j<=n;j++)
dp[i][j]=-0x3f3f3f3f;
for(int i=1;i<=k;i++){
int l1=1,r1=0;
for(int j=max(1,e[i].s-e[i].l+1);j<=e[i].s;j++){
while(l1<=r1&&dp[i-1][j-1]-e[i].p*j>=q[r1].value) r1--;
q[++r1].value=dp[i-1][j-1]-e[i].p*j;
q[r1].p1=j;
}
for(int j=1;j<=n;j++){
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
if(j>=e[i].s){
while(l1<=r1&&q[l1].p1<j-e[i].l+1) l1++;
if(l1<=r1) dp[i][j]=max(dp[i][j],q[l1].value+e[i].p*(j+1));
}
}
}
cout<<dp[k][n]<<endl;
return 0;
}
1.1单调队列优化多重背包
I P3423 [POI2005]BAN-Bank Notes
代码:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效