尺取法入門經典——2019暑假篇

一.尺取法

1.定義

尺取法,顧名思義,就是規定一個尺子,也就是一個限制條件,然後在一個序列中找滿足這個條件的子序列。顯然易懂,代碼也不長,純屬暴力

2.實現方法

這和莫隊有一點相似。我們先規定一個左指針和右指針,從序列的第一個位置開始。如果當前指針內區間的值的和小於要求的和,就右指針往右移,如果當前指針內區間的值的和大於或等於要求的和,就左指針往右移。通常在左指針移動之後來統計區間最小長度。

3.模板

void FindSumK (){//s是限制條件,a數組是原序列
	ans = INF;
	l = r = 1;
	sum = a[1];
	while (r <= n){
		while (sum < s && r <= n){
			r ++;
			sum += a[r];
		}
		while (sum >= s){
			sum -= a[l];
			l ++;
		}
		ans = min (ans, r - l + 2);//很明顯,左指針前面的那一個數到右指針之間的和大於或等於要求和,所以區間長度應爲r - (l - 1) + 1 = r - l + 2
	}
}

很簡單吧!

二.板題

1.Subsequence

 1.題目

點擊打開鏈接

 2.題解 

這裸的尺取,直接敲。

 3.Code

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

#define M 100005
#define INF 0x3f3f3f3f
#define LL long long

int T, n, s, a[M], l, r, ans;
LL sum;

void FindSumK (){
	ans = INF;
	l = r = 1;
	sum = a[1];
	if (sum >= s){
		ans = 1;
		return ;
	}
	while (r <= n){
		while (sum < s && r <= n){
			r ++;
			sum += a[r];
		}
		while (sum >= s){
			sum -= a[l];
			l ++;
		}
		ans = min (ans, r - l + 2);
	}
}
int main (){
	scanf ("%d", &T);
	while (T --){
		memset (a, 0, sizeof a);
		scanf ("%d %d", &n, &s);
		LL ss = 0;
		for (int i = 1; i <= n; i ++){
			scanf ("%d", &a[i]);
			ss += a[i];
		}
		if (ss < s){
			printf ("0\n");
			continue;
		}
		FindSumK ();
		printf ("%d\n", ans);
	}
	return 0;
}

2.Bound Found

  1.題目

點擊打開鏈接

 2. 題解

這道題目就沒有這麼簡單了,原序列它是不滿足單調性的。

但是要注意“絕對值”這三個字,假如我們統計了一個前綴和sum,那麼| sum[r] - sum[l] | = | sum[l] - sum[r] |

所以說,我們可以把前綴和拿來排個序,前綴和就滿足單調性,並且不管哪兩個前綴和相減,都能滿足題意,它都是一個區間。

問題就巧妙地解決了。

但是注意,不能用sum[r] - sum[l - 1]來計算區間和,因爲sum[l]和sum[l - 1]這兩個前綴和在原序列中可能沒有挨在一起!!!

所以一開始賦值l = 0,r = 1,到時候直接sum[r] - sum[l]就行了。

3.Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define M 100005

struct node {
	int id, v;
}sum[M];
int n, k, ansl, ansr, a, t, ans;//別用long long,我用都錯了

int fabs (int x){
	return x < 0 ? -x : x;
}
bool cmp (node a, node b){
	return a.v < b.v;
}
void FindSumK (int order){
	int l = 0, r = 1, minn = 0x3f3f3f3f;
	while (minn && r <= n){
		int ss = sum[r].v - sum[l].v;
		if (fabs (ss - order) < minn){
			minn = fabs (ss - order);
			ans = ss;
			ansl = min (sum[l].id, sum[r].id);
			ansr = max (sum[l].id, sum[r].id);
			ansl ++;
		}
		if (ss < order)
			r ++;
		if (ss > order)
			l ++;
		if (r == l)
			r ++;
	}
}
int main (){
	while (scanf ("%d %d", &n, &k)){
		memset (sum, 0, sizeof sum);
		if (! n && ! k)
			break;
		for (int i = 1; i <= n; i ++){
			scanf ("%d", &a);
			sum[i].v = sum[i - 1].v + a;
			sum[i].id = i;
		}
		sort (sum, sum + 1 + n, cmp);
		while (k --){
			scanf ("%d", &t);
			FindSumK (t);
			printf ("%d %d %d\n", ans, ansl, ansr);
		}
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章