문제
N개의 수로 된 수열 A[1], A[2], …, A[N] 이 있다. 이 수열의 i번째 수부터 j번째 수까지의 합 A[i]+A[i+1]+…+A[j-1]+A[j]가 M이 되는 경우의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N(1≤N≤10,000), M(1≤M≤300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.
출력
첫째 줄에 경우의 수를 출력한다.
예제 입력, 출력
예제 입력 | 예제 출력 | |
1 | 4 2 1 1 1 1 |
3 |
2 | 10 5 1 2 3 4 2 5 3 1 1 2 |
3 |
Two Pointer
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
// 수들의 합 2 : https://www.acmicpc.net/problem/2003
// 투포인터
/*
샘플 입력
10 5
1 2 3 4 2 5 3 1 1 2
샘플 출력
3
*/
public class Main {
public static void main(String[] args) throws Exception{
int N = 0; // 숫자의 개수
int M = 0; // 주어진 합계
int[] num; // 주어진 숫자 배열
// 입력값 처리
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
num = new int[N+1];
for (int i = 1 ; i<=N ; i++) {
num[i] = Integer.parseInt(st.nextToken());
}
// System.out.println(Arrays.toString(num));
// 투포인터로 배열 탐색 (이중으로 for 를 돌리면 시간 초과 나올 듯)
int pointerA = 1; // 초기 시작 지점 설정
int pointerB = 1; // 초기 시작 지점 설정
int cnt = 0; // 숫자의 합이 입력받은 M 과 일치한 횟수를 저장
int sum = num[pointerA]; // 시작 포인터의 값을 초기 sum 에 더하고 시작
// 두개의 포인터가 배열 끝에 도착할 때 까지 반복
while (pointerB <= N){
// System.out.println("pointerA : " + pointerA + " pointerB : " + pointerB + " cnt : " + cnt + " sum : " + sum+ " M : " + M);
if (sum > M) { // 합계가 더 큰 경우 A에 있는 값을 sum 에서 빼고 A포인터를 오른쪽으로 이동
sum -=num[pointerA];
pointerA++;
}else if (sum == M) {
cnt++; // 합계와 문제에서 주어진 값이 같은 경우 결과 카운트 1 증가
pointerB++; // B 포인터를 오른쪽으로 이동
if (pointerB <= N) { // B 포인터가 배열을 벗어나지 않은 경우에만
sum +=num[pointerB]; // sum에 현재 배열의 값을 합산
}
}else if (sum < M) {
pointerB++; // B 포인터를 오른쪽으로 이동
if (pointerB <= N) { // B 포인터가 배열을 벗어나지 않은 경우에만
sum +=num[pointerB]; // sum에 현재 배열의 값을 합산
}
}else {
System.out.println("이런 경우는 없어요");
}
}
System.out.println(cnt);
}
}