DP的优化 1

1.0单调队列

在计算形如dp[i]=max(dp[j])+C O1 max(dp[j]),那么时间复杂度就会大大降低,O(n2)O(n)

显然在max(dp[j])中,只有最大值有用,所以我们就可以利用单调队列,维护区间最大值,进行求解.

例题:

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。定义dp[i][j]为前i个人涂前j块木板的最大答案.

考虑当前这个人涂或者不涂,是否涂第j块木板容易写出

dp[i][j]=max(dp[i1][j],dp[i][j1])

dp[i][j]=maxjl[i]+1ks[i]1(dp[i1][k1]+(jk+1)p[i])(kkj)

dp[i][j]=maxjl[i]+1ks[i]1(dp[i1][k1]kp[i])+(j+1)p[i](分离决策变量k和状态变量j)

注意到k的作用就是枚举求出对应j的区间最值,于是有

当我们转移dp[i][j],循环到j时,注意到j每加一位,区间也会随之向右线性变化.

只需利用单调队列预先处理出这个包含s[i]的区间最值.再动态维护合法区间即可.

一道简单题还调了好久

代码

点击查看代码
#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

代码:

2.斜率优化

posted @   sin_wt  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示