首页 > 其他分享 >单调队列优化DP

单调队列优化DP

时间:2023-06-14 17:36:10浏览次数:48  
标签:队列 int DP 当前 我们 单调 define

单调队列优化DP

单调栈和单调队列都是借助单调性,及时排除不可能的决策,保持候选集合的高度有效性和秩序性。单调队列尤其适合优化决策取值范围的上、下界均单调变化,每个决策在候选集合中插入或删除至多一侧的问题。

利用单调队列,我们可以舍去许多无用的状态,来更快的找出最优解。

持续更新题目中,约为三天

最大子序和

显然最朴素的方法是直接暴力,但是 \(3\times 10^{5}\) 的数据范围是不会让我们 AC 的。

我们考虑利用单调队列来优化一下复杂度。

首先我们看到求连续的一段的和,所以我们先预处理出前缀和。

首先我们知道如果当前遍历到的元素的下标减去队列头的元素是大于 \(m\) 的话,这个是不合法的,所以我们每到了一个点都要先把这些不合法的都给弹出去,然后在进行求解。

我们维护一个单调递增的队列,这样我们的头部元素保证是这个区域内最优的一块,然后我们统计答案直接用当前点的前缀和减去头部第一个元素的下标对应的前缀和就好了。

code:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
#define N 1000100
using namespace std;
int n,m,a[N],sum[N],q[N],h,t,ans=-INF;
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=1;i<=n;i++)
	{
		while(h<=t&&i-q[h]>m)h++;
		ans=max(ans,sum[i]-sum[q[h]]);
		while(h<=t&&sum[q[t]]>=sum[i])t--;
		q[++t]=i;
	}
	cout<<ans<<endl;
	return 0;
}

围栏

我们设 \(f[i][j]\) 表示前 \(i\) 个人粉刷前 \(j\) 块木板的最大花费。

我们对于每一个人,枚举每一块木板,我们都有以下三种情况:

  1. 当前人不粉刷,f[i][j]=max(f[i-1][j],f[i-1][j])

  2. 当前人粉刷,但是不粉刷当前木板 f[i][j]=max(f[i][j-1],f[i][j])

  3. 当前人粉刷且粉刷当前木板、

第三种情况又分为两种情况。

  1. 当前的 \(j<s_{i}\),只能做左端点,因为必须包含 \(s_{i}\),所以这个时候,我们维护一个以队列里的点为起点时花费最大的花费单调递增的队列,也就是说,我们队列里面是左端点,但是我们里面单调递增的,是未计算当前粉刷的区间时最大的花费。

  2. 当前点 \(j>=s_{i}\),只能做右端点,这个时候我们判断一下队列里的元素是否和当前点的最大长度不超过 \(l\),然后我们进行计算,设我们从里面取出的左端点为 \(k\),则 f[i][j]=max(f[i][j],f[i-1][k]+(j-k)*p_{i})

然后就得出答案了。

code:

#include<bits/stdc++.h>
#define N 100010
#define M 110
using namespace std;
int n,m,q[N],f[M][N];
struct sb{int l,p,s;}e[N];
inline int cmp(sb a,sb b){return a.s<b.s;}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>e[i].l>>e[i].p>>e[i].s;
	sort(e+1,e+m+1,cmp);//按必须包含的木板s来从小到大排序 
	for(int i=1;i<=m;i++)//枚举每一个工匠 
	{
		int h=0,t=0;//头尾指针 
		for(int j=0;j<=n;j++)
		{
			f[i][j]=f[i-1][j];//当前工人不用 
			if(j)f[i][j]=max(f[i][j],f[i][j-1]);//当前木板不粉刷 
			int l=e[i].l,p=e[i].p,s=e[i].s;//区间最大长度,单位花费,必须包含的木板 
			if(h<=t&&q[h]<j-l)h++;//如果要是队列里最靠左的元素在当前的区间左边,我们直接弹出 
			if(j>=s&&h<=t)//如果要是当前点大于s且队列不空,那j就只能是区间右端点
			{
				int k=q[h];//取出队头元素
				f[i][j]=max(f[i][j],f[i-1][k]+p*(j-k));//取max
			}
			if(j<s)//如果要是当前点不包含s就只能做左端点 
			{
				while(h<=t&&f[i-1][q[t]]-q[t]*p<=f[i-1][j]-j*p)t--;//如果以当前的尾部元素为起点的话不如以当前点为起点更优就弹出,维护队列内的起点的价值单调递增 
				q[++t]=j;//入列 
			}
		}
	}
	cout<<f[m][n]<<endl;//输出m个工匠粉刷n个木板的最大收益 
	return 0;
}

P1725 琪露诺

我们观察题目,发现如果要是从一点点转移到一个区间是不好转移的,所以我们转化一下问题,变为当前点由那些点转移过来,这样我们就可以由已经处理好的问题来处理当前的问题了。

我们手模一下可以发现,点 \(i\) 只能由 \([i-R,i-L]\) 转移而来,所以我们直接用一个单调队列来维护里面的 \(f[k]\) 单调递减,也就是说,我们每次到了点 \(i\) 都把当前最大能转移到 \(i\) 的点给放入队列,然后维护一个单调递减的队列,这样让我们的头部元素始终是最优的,我们就可以通过头部元素转移到当前点;当然我们也要判断是否在这个区间内,把不合法的先弹出去。

code:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define int long long
#define N 1000100
using namespace std;
int n,L,R,a[N],f[N],q[N],h,t=-1,ans=-INF;
signed main()
{
	cin>>n>>L>>R;
	for(int i=0;i<=n;i++)cin>>a[i];
	memset(f,-127,sizeof f);
	f[0]=0;
	for(int i=L;i<=n;i++)
	{
		while(h<=t&&f[q[t]]<=f[i-L])t--;
		q[++t]=i-L;
		while(h<=t&&q[h]<i-R)h++;
		f[i]=f[q[h]]+a[i];
//		for(int j=h;j<=t;j++)cout<<f[q[j]]<<" ";cout<<endl;
		if(i>n-R)ans=max(ans,f[i]);
	}
	cout<<ans<<endl;
	return 0;
}

标签:队列,int,DP,当前,我们,单调,define
From: https://www.cnblogs.com/Multitree/p/17480223.html

相关文章

  • TCP/UDP的一些区别
    TCP服务端创建TCP连接,其作用是监听来自其他IP的连接请求,所以设置的参数有两个1.需要监听的IP地址,如果设置为0.0.0.0则是监听所有地址2.监听端口,注意这里端口是服务端本身的端口,可以理解为服务端这座屋子选择开哪个门迎客当连接完成后,服务端自动获取来自客户端的端口信息......
  • wordpress从word复制粘贴公式
    ​ 如何做到ueditor批量上传word图片?1、前端引用代码<!DOCTYPE html PUBLIC "-//W3C//DTDXHTML1.0Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>......
  • docker搭建wordpress
    ==========================docker的安装与部署==========================dockerimages 查看镜像dockerps-a 查看当前已有容器状态dockerexec-it容器编码(无重复前三位即可)或容器名称=================容器有点像虚拟机docker服务秒级启动finalshell连接centos7=......
  • 如何查看在当前的ingress-controller中,有哪些backend?每个backend的endpoints是什么?
    通过kubectlingress-nginx命令,可以查看在ingresscontroller中,有哪些backends,每个backends的后端的endpoints信息和对应其他的参数设置 比如: kubectlingress-nginxbackends-ningress-nginx  [root@nccztsjb-node-23data]#kubectlingress-nginxbackends-n......
  • python对接事务性MSMQ队列
    研究了很久,逐步了解到原理后,发现python发送消息到事务性msmq肯定可行。现在能搜到的资源没有任何一篇文章说明了这个,包括gpt都一样。废话不多说,直接上代码 importwin32com.client#关键代码必须使用gencache导入"MSMQ.MSMQQueueInfo"win32com.client.gencache.Ensure......
  • 数位 DP
    引入数位是指把一个数字按照个、十、百、千、万等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是$0\sim9$。数位DP一般是用来解决一类特定问题,以1012.NumbersWithRepeatedDigits(Hard)为例,这一类问题的特征非常明显要求统计满足一定......
  • v831-openwrt-c-多线程、队列篇
    前言这几天都在搞多线程和队列,但是最后发现由于v831的单核,用了多线程和队列还不如不用,并且吐槽一下c的线程和队列库,特别队列库很难用。线程库#include<pthread.h>      //系统的多线程文件使用条例:使用的很简单,网上的说明很清楚,不需要详细说明指向感悟很鸡肋......
  • 【大数据】大数据 Hadoop 管理工具 Apache Ambari(HDP)
    目录一、概述二、Ambari与HDP关系三、Ambari与Clouderamanager的对比1)开源性2)支持的发行版3)用户界面4)功能和扩展性5)社区支持和生态系统四、ApacheAmbari术语五、ApacheAmbari核心组件介绍六、ApacheAmbari架构1)Ambari-agent内部架构2)Ambari-server内部架构3)Ambari......
  • SystemVerilog练习(结构体加队列)
           《SystemVerilog验证测试平台编写指南》,刚刚学完队列和结构体,就想练习一下。1moduleTestStruct;2typedefstructpacked3{4bit[7:0]addr;5bit[7:0]pr;6bit[15:0]data;7}Packet;89Pac......
  • 926.将字符串翻转到单调递增
    问题描述926.将字符串翻转到单调递增(Medium)如果一个二进制字符串,是以一些0(可能没有0)后面跟着一些1(也可能没有1)的形式组成的,那么该字符串是单调递增的。给你一个二进制字符串s,你可以将任何0翻转为1或者将1翻转为0。返回使s单调递增的最小翻转次数。示例......