(纪中)3463. 军训【单调队列】【DP】【二分】

66 篇文章 1 订阅
17 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述


解题思路
题目中要求最小值的最大值,这种求最大最小双最值得问题可以很容易想到用二分答案来做.那对于判断 a n s ans ans c h e c k check check是否能用贪心求解?显然对于某一连续段而言,加入一个学生,可以增加其女友指数,但也有可能使得欠扁值增大,并没有什么能够保证正确性和最优性的贪心,那么可以考虑DP,则可得到转移方程如下:(dp[i]表示到第i个学生时,最大的欠扁值)
在这里插入图片描述
显然转移一次复杂度为O(n),总的时间复杂度为O(n^2),显然是会超时的.
但是实际上并非所有的转移都是有效的,有的实际上是无用的,可以用单调队列来维护一个H单调递减的序列,便可以达到减少转移个数实现优化的目的.
在这里插入图片描述

将pos(位置)和H(欠扁值)放在二维坐标轴上,当前i点,可以从直线p以后的点转移过来(p表示满足二分的答案下距i最远的分班位置)。


代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#define ll long long
#define ldb long double
using namespace std;

int n,limit,l,r,ans,H[20010],g[20010],q[20010],sum[20010],dp[20010];

bool check(int mid){
	int p=1,h=0,t=1;//p表示能够满足ans的分段临界位置
	for(int i=1;i<=n;i++)
	{
		while(sum[i]-sum[p-1]>mid)p++;//临界位置向右移动
		while(h<=t&&q[h]<p)h++;	//临界位置的点不在新班中,不再考虑他们的欠扁值,出队
		while(h<=t&&H[q[t]]<=H[i])t--;	//要将i入队,为维护队列单调递减,H比i小的出队
		q[++t]=i;//i入队
		dp[i]=dp[p-1]+H[q[h]];//初值(不在h~t中分班)
		for(int j=h;j<t;j++)
			dp[i]=min(dp[i],dp[q[j]]+H[q[j+1]]);//使该点的dp值尽可能小
		if(dp[i]>limit)return 0;//若最小dp值超过限制,ans不存在
	}
	return 1;
}

int main(){
	scanf("%d%d",&n,&limit);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&H[i],&g[i]); 
		l=max(l,g[i]);
		r=r+g[i];
		sum[i]=sum[i-1]+g[i]; 
	}
	while(l<r)
	{
		int mid=(l+r)/2;
		if(check(mid))
		{
			r=mid;
			ans=mid;
		
		}
		else l=mid+1;
	 } 
	printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值