PS/BOJ2022. 5. 15. 12:15

 

이번 문제는 해결법을 생각해내지 못했다.

머리속에 Q를 사용하여 각 P를 처리해야될 것 같다까지는 떠올랐지만 실패했다.

이후 구현 방법에 대한 구체가 없어서 검색을 통해 아래의 해결 방법 및 코드를 학습했다.

 

전략

1. 계단이 총2개 존재하므로, 각 계단에 대해 Q를 할당한다.

2. P의 숫자나 맵의 크기가 시간초과가 발생할 정도로 크지 않기 때문에 완전탐색(DFS)을 통해 P가 계단을 선택하는 모든 경우의 수를 시뮬레이션한다. ( 이부분을 전혀 생각해내지 못함, 이 부분만 떠올랐으면 풀어 냈을지도 )

 

3. 각 case에 대한 simulation을 진행한다.

도착시간이 지나고 Q에 빈자리가 생기면 enqueue해준다. 

다음 iteration에서 , 계단을 내려가고 있는 P에 대한 처리를 진행한다. 

계단에 진입한 이후 k시간이 지나면 Q에서 제거, 그렇지 않다면 다시 enqueue해준다.

 

모든 P가 계단에 진입하고(cnt), q1/q2가 empty이면 모든 상황이 종료된다. 

 

유사한 문제가 나왔을 때 활용할 수 있도록 아래의 코드형태를 잘 기억하는게 좋을것 같다.

 

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;

class P {
	int x, y;
	int arrivalTime;// 계단 도착시간
	int stair;// 계단 종류
	int stairTime;// 계단을 내려가기 시작한 시각

	P(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void setArrivalTime(S s) {
		arrivalTime = Math.abs(x - s.x) + Math.abs(y - s.y);
	}

}

class S {
	int x, y, k, cnt;

	S(int x, int y, int k) {
		this.x = x;
		this.y = y;
		this.k = k;
	}
}
class Solution {

	static int N;
	static int[][] map;
	static ArrayList<P> pList;
	static ArrayList<S> sList;
	static boolean[] visited;
	static Queue<P>[] Q;
	private static int ans;

	public static void main(String args[]) throws Exception {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int T;
		T = Integer.parseInt(br.readLine());

		StringTokenizer st = null;
		StringBuilder sb = new StringBuilder();
		long startTime = System.currentTimeMillis();
		for (int test_case = 1; test_case <= T; test_case++) {
			st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken());
			pList = new ArrayList<>();
			sList = new ArrayList<>();
			for (int i = 0; i < N; i++) {
				st = new StringTokenizer(br.readLine());
				for (int j = 0; j < N; j++) {
					int v = Integer.parseInt(st.nextToken());
					if (v == 1) {
						pList.add(new P(i, j));
					} else if (v != 0) {
						sList.add(new S(i, j, v));
					}
				}
			}

			// init
			ans = Integer.MAX_VALUE;
			Q = new LinkedList[2];
			Q[0] = new LinkedList<>();
			Q[1] = new LinkedList<>();

			go(0);
			System.out.println("#" + test_case + " " + ans);
		}
	}

	private static void go(int level) {

		if (level == pList.size()) {
			visited = new boolean[pList.size()];
			int cur = simulation();
			ans = Math.min(cur, ans);
			return;
		}

		pList.get(level).stair = 0;
		pList.get(level).setArrivalTime(sList.get(0));
		go(level + 1);
		pList.get(level).stair = 1;
		pList.get(level).setArrivalTime(sList.get(1));
		go(level + 1);

	}

	private static int simulation() {
		int cnt = 0;
		int time = 0;

		while (true) {
			// Q안의 P중, 도착시간 + K가 현재시간과 같아지면, 제거한다.
			for (int i = 0; i < 2; i++) {
				int size = Q[i].size();

				for (int j = 0; j < size; j++) {
					P p = Q[i].poll();
					S s = sList.get(i);
					if (p.stairTime + s.k <= time)
						continue;

					Q[i].offer(p);
				}
			}

			if (cnt == pList.size() && Q[0].isEmpty() && Q[1].isEmpty()) {
				return time;
			}

			// Q안에 대기가 3개 미만이면 P를 넣어준다.
			for (int i = 0; i < pList.size(); i++) {
				P p = pList.get(i);
				Queue<P> q = Q[p.stair];
				if (visited[i])
					continue;

				if (p.arrivalTime + 1 <= time && q.size() < 3) {
					visited[i] = true;
					p.stairTime = time;
					q.offer(p);
					cnt++;
				}
			}
			time++;
		}
	}

}

 

 

참조 : https://velog.io/@hyeon930/SWEA-2383-%EC%A0%90%EC%8B%AC-%EC%8B%9D%EC%82%AC%EC%8B%9C%EA%B0%84-Java

'PS > BOJ' 카테고리의 다른 글

4014 활주로 탐색  (0) 2022.05.15
4013 특이한 자석  (0) 2022.05.15
2382 미생물 격리  (0) 2022.05.14
2117 홈 방범 서비스  (0) 2022.05.14
2112 보호필름  (0) 2022.05.14
Posted by easy16
PS/BOJ2022. 5. 14. 23:04

풀이 전략

1. BFS를 사용하며 시간의 흐름(M)를 구분함. (굳이 Q를 사용할 이유는 없다. 그저 내가 편하기 때문에 사용함.)

2. 각 군의 위치를 PriorityQueue에 넣어 매 시간의 흐름마다 위치를 계산함. 이때 계산된 값을 버퍼에 저장하여 다른 각군의 움직임에 따른 개수와 방향을 결정한다. (비효율적이긴 하지만 디버깅할 때는 이점이 있었다.)

 

3. PriorityQueue를 사용 or 정렬을 사용하지 않는다면, 다수의 군이 합쳐지는 경우, 방향이 제대로 결정되지 않아 오답이 나올 수 있다.

 

int Array에 대한 정렬은 아래와 같이 구현 가능하다. (size 및 lambda 식을 인자로 전달하면 된다)

PriorityQueue<int[]> Q = new PriorityQueue<>(4, (a, b) -> {
	return b[2] - a[2];
});

 

 

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;

class Solution {

	static int N, M, K;
	static int[][][] map;
	static ArrayList<int[]> list;
	private static boolean DBG = false;
	static int[] dx = { 0, -1, 1, 0, 0 };
	static int[] dy = { 0, 0, 0, -1, 1 };
	static int num;

	public static void main(String args[]) throws Exception {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		if (DBG) {
			File file = new File("C:\\Users\\Lee\\eclipse-workspace\\TestApplication\\output.txt");
			PrintStream ps = new PrintStream(new FileOutputStream(file));
			System.setOut(ps);
		}

		int T;
		T = Integer.parseInt(br.readLine());

		StringTokenizer st = null;
		StringBuilder sb = new StringBuilder();
		long startTime = System.currentTimeMillis();
		for (int test_case = 1; test_case <= T; test_case++) {
			st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken());
			M = Integer.parseInt(st.nextToken());
			K = Integer.parseInt(st.nextToken());

			// K개의 군집, 좌표 및 미생물의 수, 방향
			list = new ArrayList<int[]>();
			if (DBG)
				System.out.printf("N : %d M : %d K : %d\n", N, M, K);
			for (int i = 0; i < K; i++) {

				st = new StringTokenizer(br.readLine());
				int r = Integer.parseInt(st.nextToken());
				int c = Integer.parseInt(st.nextToken());
				int n = Integer.parseInt(st.nextToken());
				int d = Integer.parseInt(st.nextToken());

				list.add(new int[] { r, c, n, d });

			}
			num = 0;
			bfs();
			System.out.println("#" + test_case + " " + num);
		}
		if (DBG) {
			long duration = System.currentTimeMillis() - startTime;
			System.out.println("duration : " + duration + "ms");
		}
	}

	static void bfs() {
		PriorityQueue<int[]> Q = new PriorityQueue<>(4, (a, b) -> {
			return b[2] - a[2];
		});

		for (int[] ar : list)
			Q.offer(ar);

		int level = 0;

		while (!Q.isEmpty()) {
			int size = Q.size();
			// 1회 돌 때마다 map을 초기화
			map = new int[N][N][2];
			if (level == M) {
				for (int[] ar : Q) {
					num += ar[2];
				}
				if (DBG)
					System.out.printf("level : %d  M : %d num : %d\n", level, M, num);
				return;
			}

			for (int q = 0; q < size; q++) {
				int[] now = Q.poll();
				int r = now[0], c = now[1], n = now[2], d = now[3];
				int nr = r + dx[d];
				int nc = c + dy[d];
				if (nr == 0 || nr == N - 1 || nc == 0 || nc == N - 1) {
					// 방향 결정
					if (map[nr][nc][0] < n / 2) {
						map[nr][nc][1] = (d % 2 == 0) ? d - 1 : d + 1;
					}
					// 닿은 경우 절반으로 줄임
					map[nr][nc][0] += n / 2;
				} else {
					map[nr][nc][1] = map[nr][nc][0] < n ? d : map[nr][nc][1];
					map[nr][nc][0] += n;
				}

			}

			if (DBG) {
				System.out.println("level : " + level);
				for (int i = 0; i < N; i++) {
					for (int j = 0; j < N; j++) {
						System.out.printf("(%3d %3d) ", map[i][j][0], map[i][j][1]);
					}
					System.out.println();
				}
			}
			for (int i = 0; i < N; i++) {
				for (int j = 0; j < N; j++) {
					if (map[i][j][0] != 0) {
						// if (DBG)
						// System.out.printf("offer %d %d %d %d\n", i, j, map[i][j][0], map[i][j][1]);
						Q.offer(new int[] { i, j, map[i][j][0], map[i][j][1] });
					}
				}
			}

			++level;

		}

	}

}

 

다른 풀이를 보면, 굳이 Queue를 사용할 필요는 없으며 ArrayList 및 for 문 만으로도 구현 가능하다.

 

아래 코드조각을 참조하면 각군에 대해 개별로 처리를 해준 뒤, sort하여 앞뒤 요소를 비교하여 좌표가 같으면 

합쳐준 뒤, 나머지를 삭제한다.

 

				//문자열의 pos 처럼 행렬의 값을 하나로 표시
                virus.num = (virus.i * N) + virus.j;
				
                ...
                
				Collections.sort(list);

                // 이동 후 두 개 이상의 군집이 한 셀에 모이는 경우 군집들이 합쳐지게 된다.
                for (int idx = 0; idx < list.size() - 1; idx++) {
                    Virus now = list.get(idx);
                    Virus next = list.get(idx + 1);
					//같은 위치에 있을 경우, 하나로 합친뒤 나머지를 제거
                    if (now.num == next.num) {
                        now.cnt += next.cnt;
                        list.remove(idx + 1);
                        idx--;
                    }
                }

 

 

 

'PS > BOJ' 카테고리의 다른 글

4013 특이한 자석  (0) 2022.05.15
2383 점심 식사시간  (0) 2022.05.15
2117 홈 방범 서비스  (0) 2022.05.14
2112 보호필름  (0) 2022.05.14
2105 디저트 카페  (0) 2022.05.14
Posted by easy16
PS/BOJ2022. 5. 14. 18:33

 

접근

 

1. K값에 따라 영역이 퍼져나가는 특성이 있으므로 BFS를 통해 해당영역의 home이 몇개인지 count하는 방식으로 문제를 해결함.

 

2. K값의 범위에 대해 고민이 있었다. 처음에 K==N 까지만 탐색하면 모든 맵을 체크할 수 있을거라 지레 짐작 했으나, 마지막 TC에서 하나가 모자란 것을 보고 다시 생각하게 되었다. 2*N과 같이 충분히 크게해서 검사해도 괜찮으나 더 나은 성능을 위해 적당한 값으로 설정했다.

 

3. 다른 사람의 코드를 살펴보니, 미리 저장된 home의 좌표를 활용하여 검사하는 지점으로 부터의 거리 k-1 만큼 떨어지 확인하는 Solution이 있었다. 로직이 단순하고 루프가 적다보니, 확실히 속도 측면에서의 이득이 크다.

 

 

import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.InputStreamReader;

class Solution {

	static int N, M;
	static int[][] map;
	private static boolean DBG = false;

	static int max;
	static int maxIncome;
	static boolean[][] visited;

	public static void main(String args[]) throws Exception {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		// File file = new
		// File("C:\\Users\\Lee\\eclipse-workspace\\TestApplication\\output.txt");
		// PrintStream ps = new PrintStream(new FileOutputStream(file));
		// System.setOut(ps);

		int T;
		T = Integer.parseInt(br.readLine());

		StringTokenizer st = null;
		StringBuilder sb = new StringBuilder();
		long startTime = System.currentTimeMillis();
		for (int test_case = 1; test_case <= T; test_case++) {
			max = Integer.MIN_VALUE;
			st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken());
			M = Integer.parseInt(st.nextToken());

			map = new int[N][N];
			maxIncome = 0;
			for (int i = 0; i < N; i++) {
				st = new StringTokenizer(br.readLine());
				for (int j = 0; j < N; j++) {
					map[i][j] = Integer.parseInt(st.nextToken());
					if (map[i][j] == 1)
						maxIncome += M;
				}
			}

			for (int i = 0; i < N; i++) {
				for (int j = 0; j < N; j++) {
					// K는 임의로 잡았음(범위조절 필요)
					for (int k = 1; k <= N + 3; k++) {

						// 모든 자원보다 k로 인한 비용이 더 큰 경우는 세지 않는다.
						int cost = k * k + (k - 1) * (k - 1);
						if (maxIncome < cost)
							break;

						visited = new boolean[N][N];
						homeCount = 0;
						bfs(i, j, k);

						if (DBG)
							System.out.printf("i,j[%d %d]k : %d cost : %d income :%d homeCount:%d  maxIncome : %d\n", i,
									j, k, cost, homeCount * M, homeCount, maxIncome);
						if (cost <= M * homeCount) {
							max = Math.max(max, homeCount);
						}

					}
				}

			}

			System.out.println("#" + test_case + " " + max);

		}

		System.out.println(sb);

		if (DBG) {
			long duration = System.currentTimeMillis() - startTime;
			System.out.println("duration : " + duration + "ms");
		}

	}

	static int[] dx = { 1, -1, 0, 0 };
	static int[] dy = { 0, 0, 1, -1 };
	static int homeCount;

	private static void bfs(int sr, int sc, int K) {
		Queue<int[]> Q = new LinkedList<>();
		StringBuilder sb = new StringBuilder();
		Q.offer(new int[] { sr, sc });
		int level = 0;
		while (!Q.isEmpty()) {

			int size = Q.size();
			if (level == K) {
				if (DBG)
					System.out.println("K (level) is " + level);
				return;
			}
			for (int q = 0; q < size; q++) {
				int[] now = Q.poll();
				int r = now[0];
				int c = now[1];

				if (visited[r][c])
					continue;
				visited[r][c] = true;

				if (map[r][c] == 1)
					++homeCount;

				for (int i = 0; i < 4; i++) {
					int nr = r + dx[i];
					int nc = c + dy[i];
					if (nr >= 0 && nr < N && nc >= 0 && nc < N && !visited[nr][nc]) {
						Q.offer(new int[] { nr, nc });
					}
				}

			}
			++level;
		}

	}

}

 

참고 코드.

    static int security(int r, int c, int k) {
        int count = 0;
        for(int i=0; i<house.size(); i++) {
            House h = house.get(i);
            if(Math.abs(h.row-r) + Math.abs(h.col - c) <= k-1) count++;
        }
        return count;
    }

 

'PS > BOJ' 카테고리의 다른 글

2383 점심 식사시간  (0) 2022.05.15
2382 미생물 격리  (0) 2022.05.14
2112 보호필름  (0) 2022.05.14
2105 디저트 카페  (0) 2022.05.14
1953 탈주범 검거  (0) 2022.05.13
Posted by easy16