투 포인터

간단 정리

 

두 개의 포인터를 사용하여 풀이한다.

하나의 배열에서 풀이하는 문제와 두 개의 배열에서 풀이하는 문제가 있다.

핵심은 포인터를 어떤 조건에서 움직일 것인가를 명확히 하는 것이다.

 

두 개의 포인터와 조건을 다 따로 보려고 하면 분기점이 많아지므로

오른쪽 포인터를 고정시키는 테크닉과 오른쪽으로만 이동할 수 있는 설계가 필요하다.

 

 


 

 

예제

 

겹치는 건 싫어

https://www.acmicpc.net/problem/20922

 

20922번: 겹치는 건 싫어

홍대병에 걸린 도현이는 겹치는 것을 매우 싫어한다. 특히 수열에서 같은 원소가 여러 개 들어 있는 수열을 싫어한다. 도현이를 위해 같은 원소가 $K$개 이하로 들어 있는 최장 연속 부분 수열

www.acmicpc.net

import sys

def get_answer(N, K, numbers):
    records = [0] * 100001
    max_partial_length = 0
    left_idx = 0

    for right_idx in range(N):
        num = numbers[right_idx]
        if records[num] == K:
            max_partial_length = max(max_partial_length, right_idx - left_idx)
            while numbers[left_idx] != numbers[right_idx]:
                records[numbers[left_idx]] -= 1
                left_idx += 1
            left_idx += 1
            records[num] -= 1
        records[num] += 1

    max_partial_length = max(max_partial_length, N - left_idx)
    return max_partial_length

def sol():
    input = sys.stdin.readline
    N, K = map(int, input().split())
    numbers = list(map(int, input().split()))
    answer = get_answer(N, K, numbers)
    print(answer)

 

 

 


 

 

표절

https://www.acmicpc.net/problem/2428

 

2428번: 표절

첫째 줄에 제출한 솔루션의 개수 N이 주어진다. 둘째 줄에는 각 솔루션 파일의 크기 size(F1), size(F2), ..., size(FN)이 주어진다. (1 ≤ N ≤ 100,000, 1 ≤ size(Fi) ≤ 100,000,000) 솔루션 파일의 크기는 정수이

www.acmicpc.net

 

import sys


input = lambda: sys.stdin.readline().rstrip()
N = int(input())
files = [*map(int, input().split())]

files.sort()

answer = 0
p1 = 0
for p2 in range(N):
    cut_off = files[p2] * 0.9
    while p1 < N and cut_off > files[p1]:
        p1 += 1
    answer += p2 - p1

print(answer)

 

 


 

두 큐 합 같게 만들기

https://school.programmers.co.kr/learn/courses/30/lessons/118667

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

def solution(queue1, queue2):
    length = len(queue1)
    window_length = length*2
    pointer = length-1
    s1, s2 = sum(queue1), sum(queue2)
    
    answer = 0
    q = queue1 + queue2 + queue1 + queue2
    for i in range(window_length):
        while pointer < window_length+i and s1 < s2:
            pointer += 1
            answer += 1
            
            s1 += q[i+pointer]
            s2 -= q[i+pointer]
        
        if s1 == s2:
            return answer
        
        s2 += q[i+window_length]
        s1 -= q[i]
        pointer -= 1
        answer += 1
    
    return -1
import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int solution(int[] queue1, int[] queue2) {
        int length = queue1.length;
        int totalLength = length * 2;
        
        Queue<Integer> q1 = new LinkedList<Integer>();
        Queue<Integer> q2 = new LinkedList<Integer>();
        
        long sum1 = 0, sum2 = 0;
        for (int i=0; i<length; i++) {
            q1.add(queue1[i]);
            q2.add(queue2[i]);
            
            sum1 += queue1[i];
            sum2 += queue2[i];
        }
        
        int answer = 0;
        int maxAnswer = totalLength * 2;
        while (answer < maxAnswer) {
            if (sum1 < sum2) {
                sum1 += q2.peek();
                sum2 -= q2.peek();
                
                q1.add(q2.poll());
                
                answer++;
            } else if (sum1 > sum2) {
                sum1 -= q1.peek();
                sum2 += q1.peek();
                
                q2.add(q1.poll());
                
                answer++;
            } else {
                return answer;
            }
        }
        
        return -1;
    }
}

위 세 문제는 모두 동일한 로직으로 구현했다.

오른쪽 포인터를 고정시키고 왼쪽 포인터를 움직일 수 있도록 조건을 구현하는 방법이다.

 

 


 

 

회전초밥

https://www.acmicpc.net/problem/15961

 

15961번: 회전 초밥

첫 번째 줄에는 회전 초밥 벨트에 놓인 접시의 수 N, 초밥의 가짓수 d, 연속해서 먹는 접시의 수 k, 쿠폰 번호 c가 각각 하나의 빈 칸을 사이에 두고 주어진다. 단, 2 ≤ N ≤ 3,000,000, 2 ≤ d ≤ 3,000, 2

www.acmicpc.net

import sys


input = lambda: sys.stdin.readline().rstrip()
N, d, k, c = map(int, input().split())
foods = [int(input()) for _ in range(N)]
window_size = len(foods)
foods += foods

unique = 0
eaten = [0] * (d+1)
for i in range(k):
    curr = foods[i]
    if not eaten[curr]:
        unique += 1
    eaten[curr] += 1
max_value = unique
if not eaten[c]:
    max_value += 1

for i in range(window_size):
    left = foods[i]
    eaten[left] -= 1
    if not eaten[left]:
        unique -= 1

    right = foods[i + k]
    if not eaten[right]:
        unique += 1
    eaten[right] += 1

    if not eaten[c]:
        max_value = max(unique+1, max_value)
    else:
        max_value = max(unique, max_value)

print(max_value)

 

슬라이딩 윈도우를 사용했다.

구현이 간편한게, 첫 윈도우로 변수들을 초기화한 후에는 윈도우를 옮기며 왼쪽, 오른쪽 처리만 해주면 된다.

 

 


 

 

 

블로그

https://www.acmicpc.net/problem/21921

 

21921번: 블로그

첫째 줄에 $X$일 동안 가장 많이 들어온 방문자 수를 출력한다. 만약 최대 방문자 수가 0명이라면 SAD를 출력한다. 만약 최대 방문자 수가 0명이 아닌 경우 둘째 줄에 기간이 몇 개 있는지 출력한다

www.acmicpc.net

 

import sys


def answering():
    if max_value:
        print(max_value)
        print(answer)
        return
    print("SAD")


input = lambda: sys.stdin.readline().rstrip()
N, X = map(int, input().split())
visits = list(map(int, input().split()))

answer = 1
curr_sum = sum(visits[:X])
max_value = curr_sum
for i in range(X, N):
    curr_sum += visits[i]
    curr_sum -= visits[i-X]

    if max_value < curr_sum:
        answer = 1
        max_value = curr_sum
    elif max_value == curr_sum:
        answer += 1

answering()

 

역시 똑같이 슬라이딩 윈도우를 사용했다.

투 포인터와 슬라이딩 윈도우는 정해진 구현법 그대로 서술하는 것이 바람직하다.

'알고리즘' 카테고리의 다른 글

[알고리즘] BFS DFS  (0) 2024.03.20
[알고리즘] union-find, disjoint set  (0) 2024.03.20
[알고리즘] 문제 풀이  (0) 2024.03.18
순열과 조합을 구현해보자  (0) 2024.01.08
이진분류, 하노이 탑  (0) 2024.01.08