🎯
【Java】しゃくとり法
しゃくとり法とは
しゃくとり法とは、最適化問題や探索問題におけるアルゴリズムの一種です。ある配列の中から最適な値を、例えば、条件を満たす最長の区間や条件を満たす区間の個数等を探索する際に使用します。条件を満たす満たす場合にはしゃくを広げ、条件を満たさない場合はしゃくを縮めることにより最適解を求めます。
例題
サイズn
の配列a
の部分配列のうち、要素の合計がk
以下となる部分配列の個数を求めよ。
例題の値
n = 5
k = 6
a = [2, 3, 1, 4, 2]
例題の答え
[2], [2, 3], [2, 3, 1], [3], [3, 1], [1], [1, 4], [4], [4, 2], [2]
の10個が答えとなる部分配列です。
例題におけるしゃくとり法のアルゴリズム
- 右端のインデックスを表す変数を
r
, 現在の総和を表す変数をsum
, 答えである個数を表す変数をcount
で定義して、0
で初期化します。 - 左端のインデックスを表す変数
l
を0
からn - 1
まで繰り返すfor
文を定義します。 - 条件式が、
r
がn
より小さく、かつ、sum
にa[r]
を加えてもk
を超えないというwhile
文を定義します。 - 条件式を満たす場合、
sum
にa[r]
を加えて右端r
を1
進めます。 - 条件式を満たさない場合、
while
文を抜けて、count
にr - l
を加えます。 - 条件式が、
l == r
というif
文を定義します。 - 条件式を満たす場合、右端の
r
を1
進めます。 - 条件式を満たさない場合、
sum
からa[l]
を引きます。 -
l
を1
進めた状態で、2.
以降のfor
文に戻ります。
コード例
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n, k;
n = sc.nextInt();
k = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
// 右端のインデックスの変数を r, 現在の総和の変数を sum, 答えである個数の変数を count で定義し、0 で初期化
int r = 0, sum = 0, count = 0;
// 左端のインデックスの変数 l を 0 から n - 1 まで繰り返す
for (int l = 0; l < n; l++) {
// r が n より小さく、sum に a[r] を加えても k を超えない間
while (r < n && sum + a[r] <= k) {
// sum に a[r] を加えて右端 r を 1 進めます
sum += a[r];
r++;
}
// r - l を count に加える
count += r - l;
// もし l が r と同じなら
if (l == r) {
// r を 1 進めます
r++;
// そうでなければ
} else {
// sum から a[l] を引く
sum -= a[l];
}
}
// count を出力
System.out.println(count);
}
}
入力値
5 6
2 3 1 4 2
出力結果
10
Discussion