反复推进区间的开头与末尾,这样的方法叫做尺取法,求给定长度内的最短区间可以满足某些性质。
POJ3061
题意:
给定长度为 n 的数列整数,以及整数 S ,求出总和不小于 S 的连续子序列的长度的最小值.如果解不存在则输出 0 。
解:
不断的推进首位,每推进一次首位,就往后推近末尾直到区间序列的和大于等于S。
1 int N, S; 2 int a[MAXN]; 3 4 void solve() { 5 int res = N + 1; 6 int s = 1, t = 1, sum = 0; 7 for (;;) { 8 while (t <= N && sum < S) { 9 sum += a[t++]; 10 } 11 if (sum < S) break; 12 res = min(t - s, res); 13 sum -= a[s++]; 14 } 15 if (res > N) res = 0; 16 printf("%d ", res); 17 } 18 19 int main() { 20 #ifndef ONLINE_JUDGE 21 freopen("input.txt", "r", stdin); 22 #endif 23 int T = READ(); 24 while (T--) { 25 CLR(a); 26 scanf("%d %d", &N, &S); 27 REP(i, 1, N) scanf("%d", &a[i]); 28 solve(); 29 } 30 return 0; 31 }
POJ3320
题意:
一本书总共有 P 页,第 i 页有一个知识点 $a_i$ (每个知识点都有一个编号),求最少读连续的多少页就可以掌握所有知识点
解法:
尺取法,用一个单独的变量维护当前读过的区间内的知识数,判断其与知识总数是否相等,若相等则更新答案,否则继续推。
1 int P; 2 int a[MAXN]; 3 set<int> all; 4 5 void solve() { 6 int num = all.size(); 7 int s = 1, t = 1, sum = 0; 8 int res = P; 9 map<int, int> count; 10 for (;;) { 11 while (t <= P && sum < num) { 12 if (count[a[t++]]++ == 0) sum++; 13 } 14 if (sum < num) break; 15 res = min(t - s, res); 16 if (--count[a[s++]] == 0) sum--; 17 } 18 printf("%d ", res); 19 return; 20 } 21 22 int main() { 23 #ifndef ONLINE_JUDGE 24 freopen("input.txt", "r", stdin); 25 #endif 26 scanf("%d", &P); 27 for (int i = 1; i <= P; i++) { 28 scanf("%d", &a[i]); 29 all.insert(a[i]); 30 } 31 solve(); 32 return 0; 33 }
POJ2566
题意:
从数列中找出连续序列,使得和的绝对值与目标数之差最小。
多组目标数,数列长度为1e6
解法:
注意是要求和的绝对值,那么我们就可以先把前缀和都算出来,然后用尺取法一直推进即可。
因为是前缀和,所以不存在两个区间没有交集的情况。
代码如下:
1 int n, m, k;
2 pair<LL, int> sum[MAXN];
3
4 LL myabs(LL x) { return x >= 0 ? x : -x; }
5
6 void solve() {
7 while (m--) {
8 int s = 0, t = 1;
9 LL ans = LLINF, th, tmpans = LLINF;
10 int l, r;
11 scanf("%lld", &th);
12
13 while (t <= n) {
14 LL diff = sum[t].first - sum[s].first;
15 if (myabs(diff - th) < tmpans) {
16 tmpans = myabs(diff - th);
17 ans = diff;
18 l = sum[s].second;
19 r = sum[t].second;
20 }
21 if (diff == th) {
22 break;
23 } else {
24 diff > th ? s++ : t++;
25 }
26 if (s == t) t++;
27 }
28 if (l > r) swap(l, r);
29 printf("%lld %d %d
", ans, l + 1, r);
30 }
31 return;
32 }
33
34 int main() {
35 #ifndef ONLINE_JUDGE
36 freopen("input.txt", "r", stdin);
37 #endif
38 while (scanf("%d %d", &n, &m), n + m) {
39 sum[0].first = 0;
40 sum[0].second = 0;
41 for (int i = 1; i <= n; i++) {
42 LL x;
43 scanf("%lld", &x);
44 sum[i].first = sum[i - 1].first + x;
45 sum[i].second = i;
46 }
47 sort(sum, sum + n + 1);
48 solve();
49 }
50 return 0;
51 }
POJ2739
题意:给定一个数,求这个数可以分解为多少个连续素数的和
解法:
素数打标之后直接尺取法即可,注意这个表的下表是从1开始的
代码如下:
1 bool visit[10100000];
2 int prime[10000000];
3 int T;
4
5 int prime_num = 0;
6 void generate_prim(int n) {
7 memset(visit, true, sizeof(visit));
8 for (int i = 2; i <= n; ++i) {
9 if (visit[i] == true) prime[++prime_num] = i;
10 for (int j = 1; ((j <= prime_num) && (i * prime[j] <= n)); ++j) {
11 visit[i * prime[j]] = false;
12 if (i % prime[j] == 0) break;
13 }
14 }
15 return;
16 }
17
18 int main() {
19 #ifndef ONLINE_JUDGE
20 freopen("input.txt", "r", stdin);
21 #endif
22 generate_prim(10000 + 1);
23 while (scanf("%d", &T) && T) {
24 int cnt = 0;
25 int s = 1, t = 1, sum = 0;
26 for (;;) {
27 while (t <= prime_num && sum < T) {
28 sum += prime[t++];
29 }
30 if (sum < T) break;
31 if (sum == T) cnt++;
32 sum -= prime[s++];
33 if (prime[s] > T) break;
34 }
35 printf("%d
", cnt);
36 }
37 return 0;
38 }
POJ2100
题意:给定一个数,求这个数T( T < 1e14 )可以分解为多少个连续数的平方和,输出方案数与每个方案
解法:T开根号之后正好1e7,O( n )就可以过,所以这里用尺取法即可
1 LL T;
2 vector<vector<LL>> ans;
3
4 LL poww(LL num) { return num * num; }
5
6 bool cmp(const vector<LL>& v1, const vector<LL>& v2) {
7 return v1.size() > v2.size();
8 }
9
10 int main() {
11 while (cin >> T) {
12 // T = 2030;
13 LL s = 1, t = 1;
14 LL sum = 0;
15 LL len = (int)sqrt(T * 1.0) + 1;
16 ans.clear();
17 for (;;) {
18 while (poww(t) <= T && sum < T) {
19 sum += poww(t++);
20 }
21 if (sum < T) break;
22 if (sum == T) {
23 vector<LL> v;
24 for (int i = s; i < t; i++) {
25 v.push_back(i);
26 }
27 ans.push_back(v);
28 }
29 sum -= poww(s++);
30 if (poww(s) > T) break;
31 }
32
33 sort(ans.begin(), ans.end(), cmp);
34 printf("%d
", ans.size());
35 for (int i = 0; i < ans.size(); i++) {
36 printf("%d", ans[i].size());
37 // sort(ans[i].begin(), ans[i].end());
38 for (int j = 0; j < ans[i].size(); j++) {
39 printf(" %lld", ans[i][j]);
40 }
41 printf("
");
42 }
43 }
44 return 0;
45 }