[JAVA] 알고리즘 문제풀이 기초 - Two pointers, Sliding window -2
Two pointers, Sliding window
연속된 자연수의 합 (3-5)
-
설명 : N입력으로 양의 정수 N이 입력되면 2개 이상의 연속된 자연수의 합으로 정수 N을 표현하는 방법의 가짓수를 출력하는 프로그램을 작성하세요. 만약 N=15이면 7+8=15, 4+5+6=15, 1+2+3+4+5=15 와 같이 총 3가지의 경우가 존재한다.
-
입력 : 첫 번째 줄에 양의 정수 N(7<=N<1000)이 주어집니다.
-
출력 : 첫 줄에 총 경우수를 출력합니다.
-
작성답안 - 2point
import java.util.*;
public class Main {
public static void main(String[] args) {
Main t = new Main();
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
System.out.print(t.solution(num));
}
public int solution(int num){
int result = 0;
int[] arr = new int[num/2+2];
for(int i=0; i<num/2+2; i++){
arr[i] = i;
}
int lt=0, sum=0;
for(int rt=1; rt<arr.length; rt++){
sum += arr[rt];
if(sum==num) result++;
while(sum>num) {
sum -= arr[lt++];
if(sum==num) result++;
}
}
return result;
}
}
- 특징
- 투포인트 방식으로 시작점과 끝점을 만들어 연속수를 더하는 방식을 그대로 차용해서 사용함
- 단, 연속값을 합산할때에 중간값보다 큰 값을 두번이상 더할 일은 없으므로 배열에는 ‘num/2+2’ 값을 넣어줌
- num/2+1 값을 사용하는것이 맞으나 배열 특성상 idx가 0부터 사용되고, 편의상 idx와 값을 일치하고싶어서 한칸 더 사용함
- 투포인트 방식으로 시작점과 끝점을 만들어 연속수를 더하는 방식을 그대로 차용해서 사용함
- 작성 답안 (수학적 풀이)
import java.util.*;
public class Main {
public static void main(String[] args) {
Main t = new Main();
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
System.out.print(t.solution(num));
}
public int solution(int num){
int result = 0;
int[] arr = new int[num/2+2];
for(int i=0; i<num/2+2; i++){
arr[i] = i;
}
int div = 0;
for(int i=1; i<arr.length; i++){
div += i;
if(div>num) break;
if(i!=1 && (num-div)%i==0) result++;
}
return result;
}
}
- 풀이 답안
import java.util.*;
public class Main {
public static void main(String[] args) {
Main t = new Main();
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
System.out.print(t.solution(num));
}
public int solution(int num){
int result = 0;
int[] arr = new int[num/2+2];
for(int i=0; i<num/2+2; i++){
arr[i] = i;
}
int div = 1;
num--;
while(num>0){
num -= ++div;
if(num%div==0) result++;
}
return result;
}
}
- 특징
- 예를들어 연속되는 2개의 값으로 15를 만들수 있을 경우, 15-1-2=12를 미리 해놓은 후 12%2를 처리하면 나머지가 0이 됨. 3개로 만들수있라면 15-1-2-3=9, 9%3=0.
- 15-1-2-3-4=5, 5%4!=0 이기때문에 연속되는 4개의 값으로는 15를 만들수 없음
- 이 때 1개의 수(15)로 15를 만드는것은 연속되는 수의 합이 아니기 때문에 나의 경우 조건에 i!=1를 주가했고, 풀이답안에서는 num을 num– 처리해둠.
- 내 풀이에서는 i=1부터 계속해서 증가해가면서 (num-div)%i==0가 성립할때만 카운트를 시킴
- 풀이 답변에서는 num–에 div를 계속해서 감소시키면서 num%div==0 경우 카운트 시킴
최대 길이 연속부분수열 (3-6)
- 설명 : 0과 1로 구성된 길이가 N인 수열이 주어집니다. 여러분은 이 수열에서 최대 k번을 0을 1로 변경할 수 있습니다. 여러분이 최대 k번의 변경을 통해 이 수열에서 1로만 구성된 최대 길이의 연속부분수열을 찾는 프로그램을 작성하세요. 만약 길이가 길이가 14인 다음과 같은 수열이 주어지고 k=2라면 1 1 0 0 1 1 0 1 1 0 1 1 0 1 여러분이 만들 수 있는 1이 연속된 연속부분수열은
이며 그 길이는 8입니다.
-
입력 : 첫 번째 줄에 수열의 길이인 자연수 N(5<=N<100,000)이 주어집니다. 두 번째 줄에 N길이의 0과 1로 구성된 수열이 주어집니다.
-
출력 : 첫 줄에 최대 길이를 출력하세요.
-
작성 답안
import java.util.*;
public class Main {
public static void main(String[] args) {
Main t = new Main();
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
int chk = scan.nextInt();
int[] arr = new int[num];
for(int i=0; i<num; i++){
arr[i] = scan.nextInt();
}
System.out.print(t.solution(arr, chk));
}
public int solution(int[] arr, int chk){
int result = 0, rt;
for(int lt=0; lt<arr.length; lt++){
rt = lt+1;
int cnt=1, chg=0;
while(rt<arr.length && (arr[rt]==1 || chg<chk)){
if(arr[rt]==1) cnt++;
else {
chg++;
cnt++;
}
rt++;
}
result = Math.max(result,cnt-1);
}
return result;
}
}
- 풀이 답안
import java.util.*;
public class Main {
public static void main(String[] args) {
Main t = new Main();
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
int chk = scan.nextInt();
int[] arr = new int[num];
for(int i=0; i<num; i++){
arr[i] = scan.nextInt();
}
System.out.print(t.solution(arr, chk));
}
public int solution(int[] arr, int chk){
int result = 0, lt=0, cnt=0;
for(int rt=0; rt<arr.length; rt++){
if(arr[rt]==0) cnt++;
while(cnt>chk){
if(arr[lt++]==0) cnt--;
}
result = Math.max(rt-lt+1, result);
}
return result;
}
}
- 특징
- 나의 풀이는 매번 첫점이 이동할때마다 이후값을 다시 계산해야하는 낭비가 발생함
- lt, rt를 투포인트로 활용하고 cnt로 변경한 숫자를 집계하는 방법을 사용하는 것은 맞으나 루프를 도는 범위를 최대한 줄여야함
- 일단 길이는 매번 다시 카운트 하는것이 아니라 ‘rt-lt+1’을 길이라는 공식을 활용
- 시작점을 lt=0으로 정하고, for문을 이용해 rt만 한칸씩 증가
- arr[rt]가 0일때 변환 카운트 증가
- 그뒤 while문으로 변환카운트가 제한카운트수를 넘었을 경우 현재 시작점인 lt에서 가장 근접해있는 다음 0의 값을 찾아서 시작점을 변경하고 변환카운트 축소
- rt가 움직일때마다 rt-lt+1로 길이를 계산하고 이미 담겨있는 result와 비교해 최대값으로 반영
- 나의 풀이는 매번 첫점이 이동할때마다 이후값을 다시 계산해야하는 낭비가 발생함