Codeforces 985E Pencils and Boxes(尺取法/two pointers)

本文解析了一道关于如何合理分配铅笔到多个盒子的问题,采用排序和尺取法(two pointers)策略来确定每组铅笔的组合方式,确保每组铅笔的数量符合规定且权值差不超过限定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:

给定n,k,d,表示给你n支铅笔,每支有一个权值v。现在让你把n支笔放入一些盒子中(盒子数量可以无穷大),每个盒子中至少有k支笔,而且每个盒子中的笔的max(v)-min(v)不超过d。问你能否找到一个合法的放法,可以输出"YES",否则输出"NO"。


思路:

因为对权值差有要求,所以先进行排序,排序后能放进一个盒子的笔的权值v一定是连续的。之后记录两个值,一个是can[i],表示第i支笔能否作为某一段的终点,另一个是st[i],表示第i支笔能否作为某一段的起点。如果某支笔能作为某一段的终点,那么下一支笔一定可以当做某一段的起点,最后判断最后一支笔能否作为某一段的终点(即can[n]==1是否成立)。这样可以用尺取法(即two pointers)解决:将某一段的起点定为s,终点定为e,如果①起点所在位置可以作为某一段的起点(即st[s]==1成立);②终点到起点的距离大于等于k(即e-s+1>=k);③这一段的最大值减最小值不超过d(因为从小到大排序,所以最大值减最小值不超过d等价于a[e]-a[s]<=d)这三点成立,那么说明以s为起点,e为终点是可行的,则可以把can[e]赋值为1,把st[e+1]赋值为1。在①满足的情况下,直到③不满足之前,一直把e右移(即尺取法的思想)。

以样例2为例:

初始can[0]=1,st[1]=1(笔的下标从1开始),表示第0支笔(即一个不存在的位置)可以作为某一段的起点,第1支笔可以作为某一段的起点。(x表示第0支笔的v,占位用)


此时的s=1,e=1。判断 ①(st[s]==1)成立 ②e-s+1>=k 不成立 ③a[e]-a[s]<=d成立,则将e右移(即e++)。

此时的s=1,e=2。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d成立,则可以将can[e]=1,st[e+1]=1,,继续将e右移(即e++)。

此时的s=1,e=3。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d成立,则可以将can[e]=1,st[e+1]=1,,继续将e右移(即e++)。

此时的s=1,e=4。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d成立,则可以将can[e]=1,st[e+1]=1,,继续将e右移(即e++)。

此时的s=1,e=5。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d不成立,则退出这一次的循环。s右移(s++)。

此时的s=2,e=5。判断 ①(st[s]==1)不成立,则退出这一次的循环。s右移(s++)。

此时的s=3,e=5。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d不成立,则退出这一次的循环。s右移(s++)。

此时的s=4,e=5。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d不成立,则退出这一次的循环。s右移(s++)。

此时的s=5,e=5。判断 ①(st[s]==1)成立 ②e-s+1>=k 不成立 ③a[e]-a[s]<=d成立,则e右移(e++)。

此时的s=5,e=6。判断 ①(st[s]==1)成立 ②e-s+1>=k 成立 ③a[e]-a[s]<=d成立,则可以将can[e]=1,st[e+1]=1,,继续将e右移(即e++)。

运行到结束的过程不再赘述。

可以清晰的看出变化过程中can[n]变为了1,即可以找到一种可行方案。

如果对过程还有问题的可以具体了解尺取法或者再仔细思考这样的过程。


AC代码:

/**********************************************
	Author:		StupidTurtle
	Date:		2018.5.29
	Email:		31601359@stu.zucc.edu.cn
**********************************************/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll ;
const int oo = 0x7f7f7f7f ;
const int maxn = 5e5 + 5 ;
const int mod = 1e9 + 7 ;

int can[maxn] , st[maxn] , a[maxn] , n , k , d ;

int main(void){
	
	scanf("%d%d%d",&n,&k,&d );
	for ( int i = 1 ; i <= n ; i ++ )	scanf("%d",&a[i] );
	sort ( a + 1 , a + 1 + n );
	can[0] = 1 ;
	st[1] = 1 ;
	int e = 1 ;
	for ( int s = 1 ; s <= n ; s ++ ){
		if ( st[s] == 1 ){
			while ( a[e] - a[s] <= d && e <= n ){
				if ( e - s + 1 >= k ){
					can[e] = 1 ;
					st[e+1] = 1 ;
				}
				e ++ ;
			}
		}
	}
	
	if ( can[n] == 1 )	printf("YES\n");
	else	printf("NO\n");
	
	return 0 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值