# 유형 : 이분 탐색, LIS

# 가장 긴증가하는 부분 수열 3에서 역추적하는 배열 또는 변수를 추가하여 계산하면 된다.

테스트케이스로 

7

1 6 2 4 5 3 7 가 들어온다면 정답은 1 2 4 5 7 을 출력해야한다.

 

필자의 경우에는 기존의 리스트와 별개로 arraylist를 point형으로 한개 더 만들어서 index값과 저장되는 값을 아래와 같이 쌍으로 리스트에 넣었다.

0/1/1/2/3/2/4 

1/6/2/4/5/3/7 

이제 뒤에서부터 역추적을 해보자. 가장 maxIndex는 4이고 4->3->2->1->0(7->5->4->2->1) 순으로 maxIndex를 감소해가며 추적하는데 이 때 스택을 사용해서 push한 다음 pop하여 출력하면 편하게 결과값을 출력할 수 있다. 

 



import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Stack;
import java.util.StringTokenizer;

public class p14002 {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N = Integer.parseInt(br.readLine());
		int arr[] = new int[N];
		ArrayList arrList = new ArrayList<>();
		ArrayList tmp = new ArrayList<>();
		StringTokenizer st = new StringTokenizer(br.readLine());
		for(int i=0; i<N; i++)
			arr[i] = Integer.parseInt(st.nextToken());
		
		for(int i=0; i<N; i++) {
			int next = arr[i];
			if(arrList.isEmpty() || arrList.get(arrList.size()-1) < next) {
				arrList.add(next);
				tmp.add(new Point(arrList.size()-1,next));
			}else {
				int left = 0;
				int right = arrList.size()-1;
				while(left<right) {
					int middle =(left+right)/2;
					if(arrList.get(middle) < next)
						left = middle + 1;
					else
						right = middle;
				}
				arrList.set(right, next);
				tmp.add(new Point(right,next));
			}
			
		}
		
		int last = -1;
		for(int i=0; i<tmp.size(); i++) {
			last = Math.max(last, tmp.get(i).x);
		}
		
		Stack stack = new Stack<>();
		
		for(int i=tmp.size()-1; i>=0; i--) {
			if(last == tmp.get(i).x) {
				stack.add(tmp.get(i).y);
				last--;
			}
		}
		
		System.out.println(arrList.size());
		
		while(!stack.isEmpty())
			System.out.print(stack.pop()+" ");
		
	}
}

# 유형 : 이분 탐색, LIS

# 12015 문제와 같은 방식이지만, 주어진 숫자의 범위만 잘 확인하고 풀면 된다. LIS 문제는 세그먼트 트리로도 풀 수 있다. 

 

12015번: 가장 긴 증가하는 부분 수열 2

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)

www.acmicpc.net


package bj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class p12738 {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N = Integer.parseInt(br.readLine());
		int arr[] = new int[N];
		ArrayList arrList = new ArrayList<>();
		arrList.add(-1000000001);
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		for(int i=0; i<N; i++)
			arr[i] = Integer.parseInt(st.nextToken());
		
		for(int i=0; i<N; i++) {
			int next = arr[i];
			if(arrList.get(arrList.size()-1) < next) {
				arrList.add(next);
			}else {
				int left=0;
				int right=arrList.size()-1;
				while(left<right) {
					int middle = (left+right)/2;
					if(arrList.get(middle) < next) {
						left = middle+1;
					}else {
						right = middle;
					}
				}
				arrList.set(right, next);
			}
		}
		System.out.println(arrList.size()-1);
	}
}

# 유형 : 이분탐색, 최장 증가 수열 활용

# 가장 긴 증가하는 부분 수열1 같은 경우에는 O(N^2)로 통과할 수 있었지만, 여기서는 1000000 * 1000000 = 시간초과이다.

따라서 이분 탐색을 활용한다면 시간초과를 해결할 수 있다. 다이나믹 프로그래밍으로도 풀 수 있다.


package bj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class p12015 {
	static int N;
	static int max = 0;
	static int arr[],dp[];
	static ArrayList arrList = new ArrayList<>();
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N = Integer.parseInt(br.readLine());
		arrList.add(-99999);
		arr = new int[N];

		StringTokenizer st = new StringTokenizer(br.readLine());
		for(int i=0; i<N; i++) {
			arr[i] = Integer.parseInt(st.nextToken());
		}
		
		for(int i=0; i<N; i++) {
			int val = arr[i];
			if(arrList.get(arrList.size()-1) < val) {
				arrList.add(val);
			}else {
				int left=0;
				int right = arrList.size()-1;
				while(left<right) {
					int middle = (left+right)/2;
					if(arrList.get(middle) < val) {
						left = middle + 1;
					}else
						right = middle;
				}
				arrList.set(right, val);
			}
		}
		System.out.println(arrList.size()-1);
		
	}
}

# 유형 : 다이나믹 프로그래밍

# https://www.acmicpc.net/problem/11055, https://www.acmicpc.net/problem/11053, https://www.acmicpc.net/problem/11722 를 풀어봤다면 문제 접근은 어렵지 않다. 특정 인덱스 까지의 앞에서부터의 증가하는 부분수열 + 뒤에서부터의 증가하는 부분수열을 생각한다면 바로 풀 수 있다.

 

11722번: 가장 긴 감소하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 감소하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 30, 10, 20, 20, 10} 인 경우에 가장 긴 감소하는 부분 수열은 A = {10, 30, 10, 20, 20, 10}  이고, 길이는 3이다.

www.acmicpc.net

 

11055번: 가장 큰 증가 부분 수열

수열 A가 주어졌을 때, 그 수열의 증가 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가 부분 수열은 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 이고, 합은 113이다.

www.acmicpc.net

 


package bj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class p11054 {
	static int N;
	static int arr[];
	static int dp[];
	static int m_dp[];
	static int max=0;
	public static void main(String[] args) throws IOException {
		 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		 N = Integer.parseInt(br.readLine());
		 arr = new int[N];
		 dp = new int[N];
		 m_dp = new int[N];
		 StringTokenizer st = new StringTokenizer(br.readLine());
		 for(int i=0; i<N; i++) {
			 arr[i] = Integer.parseInt(st.nextToken());
		 }
		 for(int i=0; i<N; i++) {
			 dp[i] = 1;
			 for(int j=0; j<i; j++) {
				 if(arr[i] > arr[j]) {
					 if(dp[i] < dp[j]+1)
						 dp[i] = dp[j]+1;
				 }
			 }
		 }
		 
		 for(int i=N-1; i>=0; i--) {
			 m_dp[i]=1;
			 for(int j=N-1; j>i; j--) {
				 if(arr[i] > arr[j]) {
					 if(m_dp[i] < m_dp[j]+1)
						 m_dp[i] = m_dp[j]+1;
				 }
			 }
		 }
		 for(int i=0; i<N; i++) {
			 max = Math.max(max, dp[i]+m_dp[i]-1);
		 }
		 System.out.println(max);
		 
	}
}

# 유형 : 탐색

# 분류에 bfs라고 적혀있어서, bfs로 풀었는데 시간 초과 / dfs로 접근하였으나 범위가 너무 넓어 시간초과 /  질문검색을 통해 dp로 메모라이징하면서 dfs탐색을 하라고해서 구현하였음. 문제를 풀기 전 잘 모르겠다면 dp+dfs에 대해 한번 알아보고 풀어보길 권장함.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package bj;
 
 
public class p2186 {
    
    static int cnt=0;
    static int N,M,K;
    static char arr[][];
    static int dp[][][];
    static boolean visit[][];
    static String target;
    static int moveX[] = {0,1,0,-1};
    static int moveY[] = {-1,0,1,0};
    
    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());
        
        arr = new char[N][M];
        
        for(int i=0; i<N; i++) {
            String str = br.readLine();
            for(int j=0; j<M; j++) {
                arr[i][j] = str.charAt(j);
            }
        }
        target = br.readLine();
        dp = new int[N][M][target.length()];
        
        for(int i=0; i<N; i++) {
            for(int j=0; j<M; j++) {
                for(int k=0; k<target.length(); k++) {
                    dp[i][j][k] = -1;
                }
            }
        }
        
        for(int i=0; i<N; i++) {
            for(int j=0; j<M; j++) {
                cnt+=dfs(i,j,0);
            }
        }
        
        
        
        System.out.println(cnt);
        
    }
    
    public static int dfs(int i, int j, int index) {
        if(index == target.length()-1)
            return dp[i][j][index] = 1;
        if(dp[i][j][index] != -1)
            return dp[i][j][index];
        if(arr[i][j] != target.charAt(index))
            return dp[i][j][index] = 0;
        
        dp[i][j][index] = 0;
        
        for(int d=0; d<4; d++) {
            for(int k=1; k<=K; k++) {
                int newX = j + moveX[d]*k;
                int newY = i + moveY[d]*k;
                
                if(0<=newY && newY<&& 0<=newX && newX<&& arr[newY][newX]==target.charAt(index+1)) {
                    dp[i][j][index] += dfs(newY,newX,index+1);
                }
            }
        }
        
        return dp[i][j][index];
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

# 유형 : 탐색

# 조건문만 잘 체크하면 쉽게 해결, 맨 처음에 min 범위를 잘못 지정해서 10%에서 계속 틀려서 로직이 틀린줄 알고 한참 걸렸다. Limit 설정에 신경을 써야한다.. dfs로 탐색을 하여, count가 N개이고, 시작지점과 마지막지점이 같을때 체크해주면 된다.


package bj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class p10971 {
	static int N;
	static int arr[][];
	static int min=Integer.MAX_VALUE;
	static boolean visit[];
	public static void main(String[] args) throws NumberFormatException, IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		N = Integer.parseInt(br.readLine());
		
		arr = new int[N][N];
		visit = new boolean[N];
		for(int i=0; i<N; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			for(int j=0; j<N; j++) {
				arr[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		
		for(int i=0; i<N; i++) {
			dfs(i,i,0,0);
		}
		System.out.println(min);
		
	}
	public static void dfs(int start, int i, int cnt, int sum) {
		if(cnt == N && start==i) {
			min = Math.min(min, sum);
			return;
		}
		
		for(int idx=0; idx<N; idx++) {
			if(arr[i][idx]==0)
				continue;
			
			if(!visit[i] && arr[i][idx]>0) {
				visit[i] = true;
				sum += arr[i][idx];
				dfs(start, idx, cnt+1, sum);
				visit[i] = false;
				sum -= arr[i][idx];
			}
		}
		
		
	}
}

# 유형 : 투 포인터

# 피자개수가 최대 1000개라서, N^2로 해도 시간 초과 안나고 시간 제한에서 좀 자유롭다. 따라서 N^2으로 각 피자 조각의 합을 구한다. 이 때 조건문만 잘 지키면 어렵지 않다. 조각의 합을 구하는 조건 틀린부분을 못찾아서 한참을 생각하고 생각하였음. 조각의 합을 구하면 그 이후는 투포인터를 이용해서 구하면 됨. 이 때 투포인터 알고리즘은 A,B각각의 합으로 주어진 피자의 크기를 포함하지 않으므로 반복문을 한번씩 돌려 추가로 체크하면 된다.


package bj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

public class p2632 {
	static int N,A,B;
	static int A_[],B_[];
	public static void main(String[] args) throws NumberFormatException, IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		N = Integer.parseInt(br.readLine());
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		A = Integer.parseInt(st.nextToken());
		B = Integer.parseInt(st.nextToken());
		
		A_ = new int[A];
		B_ = new int[B];
		
		for(int i=0; i<A; i++) {
			A_[i] = Integer.parseInt(br.readLine());
		}
		for(int i=0; i<B; i++) {
			B_[i] = Integer.parseInt(br.readLine());
		}
		
		ArrayList arr_A = new ArrayList<>();
		ArrayList arr_B = new ArrayList<>();
		int low=0;
		int high=0;
		int sum=0;
		
		while(low<A_.length) {
			sum += A_[high];
			high++;
			
			if(sum<=N) {
				arr_A.add(sum);
			}else {
				low++;
				high = low;
				sum=0;
			}
			
			high%=A;
			
			
			if((low==0 && high==0) || high+1== low) {
				low++;
				high = low;
				sum = 0;
			}
		}
		
		low=0; high=0; sum=0;
		while(low<B_.length) {
			sum += B_[high];
			high++;
			
			if(sum<=N) {
				arr_B.add(sum);
			}else {
				low++;
				high = low;
				sum = 0;
			}
			
			high%=B;
			
			if((low==0 && high==0) || high+1== low) {
				low++;
				high = low;
				sum = 0;
			}
		}
		
		Collections.sort(arr_A);
		Collections.sort(arr_B);
		
		int result=0;
		int left = 0;
		int right = arr_B.size()-1;
		
		while(left<arr_A.size() && right>=0) {
			int left_val = arr_A.get(left);
			int right_val = arr_B.get(right);
			
			if(left_val + right_val == N) {
				int left_cnt=0;
				while(left<arr_A.size() && arr_A.get(left)==left_val) {
					left_cnt++;
					left++;
				}
				int right_cnt=0;
				while(right>=0 && arr_B.get(right) == right_val) {
					right_cnt++;
					right--;
				}
				result += left_cnt*right_cnt;
			}
			
			if(left_val+right_val < N) {
				left++;
			}
			if(left_val+right_val > N) {
				right--;
			}
		}
		for(int i=0; i<arr_A.size(); i++) {
			if(arr_A.get(i) == N)
				result++;
		}
		for(int i=0; i<arr_B.size(); i++) {
			if(arr_B.get(i) == N)
				result++;
		}
		
		System.out.println(result);
	}
}

'백준' 카테고리의 다른 글

#백준_2186 문자판 - Java  (0) 2020.01.23
#백준_10971 외판원 순회2 - Java  (0) 2020.01.23
#백준_1525 퍼즐 - Java  (0) 2020.01.21
#백준_7453 합이 0인 네 정수 - Java  (0) 2020.01.20
#백준_1208 부분수열의 합 2 - Java  (0) 2020.01.20

# 유형 : 탐색

# 맨 처음엔 어떻게 접근해야 많이 고민했는데, 0 을 9로 바꾸고 배열을 스트링으로 길게 뽑아서 연결하여 스트링이 123456789를 만들때까지 탐색을 하면 된다. 간단하게 말하면 

첫 번째 예제로 나와있는 아래의 값을 193425786으로 바꾼다. 그리고 9를 기준으로 삼아 계속 움직이면서 123456789로 바꾸는 데 카운트를 체크하면 된다. map을 쓰면 더 편하다.

1 0 3 

4 2 5

7 8 6

 

 

 


package bj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;

public class p1525 {
	static int arr[][];
	static int moveX[] = {0,1,0,-1};
	static int moveY[] = {-1,0,1,0};
	static Map<Integer, Integer> map = new HashMap<Integer, Integer>();
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		arr = new int[3][3];
		StringTokenizer st;
		int value = 0;
		for(int i=0; i<3; i++) {
			st = new StringTokenizer(br.readLine());
			for(int j=0; j<3; j++) {
				int val = Integer.parseInt(st.nextToken());
				if(val==0)
					val=9;
				
				value = value*10 + val;
				
				arr[i][j] = val;
			}
		}
		
		bfs(value);
		
		if(map.containsKey(123456789))
			System.out.println(map.get(123456789));
		else
			System.out.println(-1);
	}
	private static void bfs(int val) {
		Queue queue = new LinkedList<>();
		queue.add(val);
		map.put(val, 0);
		while(!queue.isEmpty()) {
			int tmp = queue.poll();
			String number = String.valueOf(tmp);
			int index = number.indexOf("9");
			
			int y = index/3;
			int x = index%3;
			
			for(int d=0; d<4; d++) {
				int newY = y + moveY[d];
				int newX = x + moveX[d];
				
				int nextIndex = newY*3 + newX;
				if(0<=newY && newY<3 && 0<=newX && newX<3) {
					StringBuilder sb = new StringBuilder(number);
					char tmpChar = sb.charAt(index);
					sb.setCharAt(index, sb.charAt(nextIndex));
					sb.setCharAt(nextIndex, tmpChar);
					
					int nextVal = Integer.parseInt(sb.toString());
					if(!map.containsKey(nextVal)) {
						map.put(nextVal, map.get(tmp)+1);
						queue.add(nextVal);
					}
				}
			}
		}
	}
}

+ Recent posts