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

传送门

题意:

给定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,占位用)

Codeforces 985E Pencils and Boxes(尺取法/two pointers)_第1张图片

此时的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++)。

Codeforces 985E Pencils and Boxes(尺取法/two pointers)_第2张图片

此时的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++)。

Codeforces 985E Pencils and Boxes(尺取法/two pointers)_第3张图片

此时的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++)。

Codeforces 985E Pencils and Boxes(尺取法/two pointers)_第4张图片

此时的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++)。

Codeforces 985E Pencils and Boxes(尺取法/two pointers)_第5张图片

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

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

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


AC代码:

/**********************************************
	Author:		StupidTurtle
	Date:		2018.5.29
	Email:		[email protected]
**********************************************/

#include 
#include 
#include 
#include 

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 ;
}

你可能感兴趣的:(cf,常用技巧-尺取)