[문제링크]

 

14503번: 로봇 청소기

로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어

www.acmicpc.net

 

0. 단순한 구현 문제

  - 2-b 수행하기 전 2-c,d먼저 조건 체크/수행해야한다

 

1. 현재 위치를 청소하며 clean 수치를 1씩 증가시킨다

 

2. 2-c,d 조건 체크를 위해 4방향 탐색

 

3. 4방향 다 벽/청소가 끝났다면 현 위치의 뒤를 확인하여 벽이라면 종료한다 (2-d)

 

4. 벽이 아니라면, 뒤로 물러난다 (2-c)

 

5. 왼쪽에 빈공간이 있다면, 회전 후 1칸 진행한다 (2-a)

 

4. 없다면, 회전한다 (2-b)

 

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

public class Main{
	
	static int[][]left= {
			{0,-1},//북쪽 기준 왼쪽
			{-1,0},//동쪽 기준 왼쪽
			{0,1},//남쪽기준
			{1,0}//서쪽기준
	};
	static int[][]back= {
			{1,0},//북쪽 기준 뒤쪽
			{0,-1},//동쪽 기준 뒤쪽
			{-1,0},//남쪽 기준 뒤쪽
			{0,1}//서쪽 기준 뒤쪽
	};
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		int n = pint(st.nextToken());
		int m = pint(st.nextToken());
		
		st = new StringTokenizer(br.readLine(), " ");
		int r = pint(st.nextToken());
		int c = pint(st.nextToken());
		int d = pint(st.nextToken());
		
		int[][]map = new int[n][m];
		
		for (int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			for (int j = 0; j < m; j++) {
				map[i][j]=pint(st.nextToken());
			}
		}
		
		int clean=0;
		while(true) {
			//1
			if(map[r][c]==0) {
				map[r][c]=2;
				clean++;
			}
			//2-c
			//2-d
			boolean isDone=true;
			for (int i = 0; i < 4; i++) {
				if(map[ r+left[i][0] ][ c+left[i][1] ]==0) {
					isDone=false;
				}
			}
			if(isDone) {//4방에 0이 없으면
				if(map[r+back[d][0]][c+back[d][1]]==1) {
					//2-D, 뒤가 벽
					break;
				}
				else {
					//2-C, 물러남
					r+=back[d][0];
					c+=back[d][1];
					continue;
				}
			}
			
			//2-a
			//왼쪽에 청소 안했으면
			if(map[r+left[d][0]][c+left[d][1]]==0) {
				r+=left[d][0];
				c+=left[d][1];
				d=(d+4-1)%4;
			}
			//2-b
			else {
				d=(d+4-1)%4;
			}
		}
		System.out.println(clean);
		
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
	
}

결과 화면

[문제링크]

 

15685번: 드래곤 커브

첫째 줄에 드래곤 커브의 개수 N(1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 드래곤 커브의 정보가 주어진다. 드래곤 커브의 정보는 네 정수 x, y, d, g로 이루어져 있다. x와 y는 드래곤 커

www.acmicpc.net

 

0. N세대 드래곤 커브란? N-1세대 드래곤 커브를 끝 점 기준 90도 회전시켜 N-1세대의 끝 점에 붙인 것

  - 한 세대마다 2배씩 증가하지만, 세대가 최대 10이므로 최대 pow(2,10), 1024개의 지점으로 구성된다

 

1. N세대 드래곤 커브를 만들기 위해선?

  - N-1세대의 끝 점에서 시작해, N-1세대 커브의 구성을 역순으로 90도 회전시켜서 적용한다

2세대 커브

  - 예를들어, 위 2세대 커브는 ↑ 순서로 이루어져 0, -2 지점에서 종료되었다.

  - 3세대 커브를 만들려면 0, -2 지점에서 2세대 커브의 역순인 → 이동을, 90도 회전시켜 ↑ 순서로 진행하면 된다

3세대 드래곤 커브

  - 이는 문제 예시를 통해 확인 가능하다

 

2. 해당 과정을 위해 N세대까지 거쳐온 기록을 Arraylist로 저장하고, 이를 역순으로 탐색하며 끝점을 이동시킨다

 

3. 끝점은 분명한 경로를 가지고 움직이지만, 문제의 요구사항은 0<=x,y<=100 인 (x,y) 좌표에 대해서 정사각형을 구하는 것이므로, 끝 점이 방문한 좌표만 기록한다

 

4. 모든 커브가 그려진 후, 0~100의 좌표들에 대해서 사각형 조건 충족 여부를 검사, 출력한다

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

public class Main{
	
	static int[][] dir= {
			{0,1},
			{-1,0},
			{0,-1},
			{1,0}
	};
	
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int cntSquare=0;
		boolean[][] map = new boolean[101][101];
		
		int N = pint(br.readLine());
		for (int i = 0; i < N; i++) {

			StringTokenizer st = new StringTokenizer(br.readLine(), " ");
			int y = pint(st.nextToken());
			int x = pint(st.nextToken());
			int d = pint(st.nextToken());
			int g = pint(st.nextToken());
			
			if(inBox(x,y)) {
				map[x][y]=true;
			}
			x+=dir[d][0];
			y+=dir[d][1];
			if(inBox(x,y)) {
				map[x][y]=true;
			}
			//curve의 경로 저장
			List<Integer>curve = new ArrayList<>();
			curve.add(d);
			
			for (int j = 0; j < g; j++) {
				for (int j2 = curve.size()-1; j2 >= 0; j2--) {
					//curve를 역순으로 탐색
					int curDir = curve.get(j2);
					//90도 회전
					int nextDir=(curDir+1)%4;
					//끝 점 이동 적용
					x+=dir[nextDir][0];
					y+=dir[nextDir][1];
					if(inBox(x,y)) {
						map[x][y]=true;
					}
					curve.add(nextDir);
				}
			}
			
		}
		
		//조건을 만족하는 사각형의 갯수 찾기
		for (int i = 0; i <100; i++) {
			for (int j = 0; j < 100; j++) {
				if(map[i][j] && map[i][j+1] && map[i+1][j] && map[i+1][j+1]) {
					cntSquare++;
				}
			}
		}
		System.out.println(cntSquare);
	}
	
	static boolean inBox(int x, int y) {
		if(x>=0 && x<=100 && y>=0 && y<=100)return true;
		return false;
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
	
}

결과 화면

 

[문제링크]

 

1922번: 네트워크 연결

이 경우에 1-3, 2-3, 3-4, 4-5, 4-6을 연결하면 주어진 output이 나오게 된다.

www.acmicpc.net

 

0. 기초적인 MST 문제 (MST란?)

 

1. PRIM 알고리즘 사용, 최소 거리로 갈수있는 & 미방문한 노드 순서로 방문한다

 

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

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		
		int N = pint(br.readLine());
		int M = pint(br.readLine());
		
		graph = new LinkedList<>();
		for (int i = 0; i <= N; i++) {
			graph.add(new LinkedList<>());
		}
		
		for (int i = 1; i <= M; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine(), " ");
			int fst = pint(st.nextToken());
			int snd = pint(st.nextToken());
			int cost = pint(st.nextToken());
			
			graph.get(fst).add(new int[] {snd, cost});
			graph.get(snd).add(new int[] {fst, cost});
		}
		
		int totalCost=0;
		int start=1;//아무거나
		
		boolean[] isVisit=new boolean[N+1];//방문체크
		int[] cost = new int[N+1]; // 0번 PC는 없다
		Arrays.fill(cost, Integer.MAX_VALUE);
		cost[start]=0;//시작 cost 초기화
		
		for (int i = 0; i < N; i++) {
			
			int min=Integer.MAX_VALUE;
			int minNode=0;
			for (int j = 1; j <= N; j++) {
				if(!isVisit[j] && cost[j]<min) {
					min=cost[j];
					minNode=j;
				}
			}
			//가장 짧은거 연결
			isVisit[minNode]=true;
			totalCost+=min;
			
			//minNode 에서 연결되있는거 갱신
			int len = graph.get(minNode).size();
			for (int j = 0; j < len; j++) {
				int[] adjNode = graph.get(minNode).get(j);
				if(!isVisit[adjNode[0]]) {
					//방문한적 없다면
					cost[adjNode[0]] = Math.min(cost[adjNode[0]], adjNode[1]);
				}
			}
		}
		System.out.println(totalCost);
		
	}
	static LinkedList<LinkedList<int[]>>graph;
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

[문제링크]

 

1647번: 도시 분할 계획

첫째 줄에 집의 개수N, 길의 개수M이 주어진다. N은 2이상 100,000이하인 정수이고, M은 1이상 1,000,000이하인 정수이다. 그 다음 줄부터 M줄에 걸쳐 길의 정보가 A B C 세 개의 정수로 주어지는데 A번 집

www.acmicpc.net

 

0. 집 사이의 연결을 유지하며 길을 제거하고, 최소 비용의 길들을 사용해 전체를 연결하기 == 최소 신장 트리(MST)

 

1. 최소 신장 트리를 구하면 최소 거리의 길들로 모든 마을을 연결할 수 있다

 

2. 완성된 MST의 간선들 중 하나를 제거하면 2개의 tree가 생성된다

  - 마을을 2개로 분리할 수 있다

 

3. 길의 유지비의 합은 항상 최소를 유지해야하므로, MST를 이루는 간선들 중 가중치가 가장 큰 간선 하나를 제거하면 마을을 분리하면서 비용을 최소로 만들 수 있다

  - Kruskal의 경우, 간선을 기준으로 최소 간선부터 고려하므로, 마지막에 선택할 간선을 선택하지 않아도 된다

  - N개의 집이 있을때, N-1개가 아닌 N-2개만 선택하는 것

 

<Kruskal 사용>

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

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		int n = pint(st.nextToken());
		int m = pint(st.nextToken());

		//간선 리스트..대신 크기가 주어지니 배열 사용
		edge[] e = new edge[m];
		for (int i = 0; i < m; i++) {
			e[i]=new edge();
			st = new StringTokenizer(br.readLine(), " ");
			
			e[i].n1=pint(st.nextToken());
			e[i].n2=pint(st.nextToken());
			e[i].cost=pint(st.nextToken());
		}
		
		Arrays.sort(e);
		
		init(n);
		
		int cnt=0, i=0;//현재 선택한 edge의 갯수
		int totalCost=0;//누적 전체cost
		while(true) {
			//union이 정상적으로 수행 됬다면
			if(union(e[i].n1, e[i].n2)) {
				//cnt, cost 반영
				cnt++;
				totalCost+=e[i].cost;
			}
			if(cnt==n-1)break;
			i++;
		}
		//최소 cost의 2개의 마을로 분할하는 법 = MST에서 가장 cost가 큰 edge 하나 자르기
		System.out.println(totalCost-e[i].cost);
	}

	//분리집합
	static int[]parents;
	static void init(int n) {
		parents=new int[n+1];
		for (int i = 0; i <= n; i++) {
			parents[i]=i;
		}
	}
	static int find(int n) {
		if(parents[n]==n)return n;
		return parents[n]=find(parents[n]);
	}
	static boolean union(int n1, int n2) {
		int p1=find(n1);
		int p2=find(n2);
		
		if(p1==p2)return false;
		parents[p1]=p2;
		return true;
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
	//간선 객체
	static class edge implements Comparable<edge>{
		int n1;
		int n2;
		int cost;
		@Override
		public int compareTo(edge e) {
			return this.cost-e.cost;
		}		
	}
}

<Prim 사용>

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		int n = pint(st.nextToken());
		int m = pint(st.nextToken());

		ArrayList<ArrayList<int[]>>graph = new ArrayList<>();
		for (int i = 0; i <= n; i++) {
			graph.add(new ArrayList<>());
		}
		//그래프 입력
		for (int i = 0; i < m; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			int x = pint(st.nextToken());
			int y = pint(st.nextToken());
			int cost = pint(st.nextToken());

			graph.get(x).add(new int[] {y,cost});
			graph.get(y).add(new int[] {x,cost});
		}
		
		int totalCost=0, cnt=0, max=-1;
		//전처리
		boolean[] isV = new boolean[n+1];
		PriorityQueue<int[]>pq = new PriorityQueue<>(new Comparator<int[]>() {
			@Override
			public int compare(int[] o1, int[] o2) {
				// TODO Auto-generated method stub
				return o1[1]-o2[1];
			}
		});
		for (int i = 1; i <= n; i++) {
			pq.offer(new int[] {i,Integer.MAX_VALUE});
		}
		//임의의 시작 노드 설정
		pq.offer(new int[] {1,0});
		
		while(cnt!=n) {
			int[] curNode = pq.poll();

			if(isV[curNode[0]])continue;//이미 방문한 node면
			
			isV[curNode[0]]=true;
			totalCost+=curNode[1];
			if(max<curNode[1])max=curNode[1];
			cnt++;
			
			for (int i = 0; i < graph.get(curNode[0]).size(); i++) {
				int[] next = graph.get(curNode[0]).get(i);
				if(!isV[next[0]]) {
					pq.offer(next);
				}
			}
			
		}
		System.out.println(totalCost-max);
		
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면(1,2 - Kruskal, 3 - Prim)

[문제링크]

 

1007번: 벡터 매칭

평면 상에 N개의 점이 찍혀있고, 그 점을 집합 P라고 하자. 집합 P의 벡터 매칭은 벡터의 집합인데, 모든 벡터는 집합 P의 한 점에서 시작해서, 또 다른 점에서 끝나는 벡터의 집합이다. 또, P에 속

www.acmicpc.net

 

0. 모든 경우의 수에 대해 계산하는 브루트-포스 방식

 

1. 벡터는 두 점 A, B가 있을때 A-B / B-A 같은 식으로 만들 수 있다

  - 한 점은 +, 한 점은 - 하게 된다

 

2. 즉, 2N개의 점으로 N개의 벡터를 만들었을때 그 벡터들의 합은 N개의 점을 +, 나머지 N개는 - 한 값이다

 

3. 점이 최대 20개인데, 20개 중 10개를 뽑는 조합의 경우의 수 20C10은 184756으로 산술 시간안에 충분히 해결 가능하다

 

4. 10개의 점을 선택해 더하고, 선택되지 않은 10개의 점을 빼는 식으로도 결과를 구할 수 있지만,

  - 산술 연산의 횟수를 줄이기 위해 모든 수를 더해두고, 10개의 점을 선택해 2번씩 빼준다

 

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

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		
		int tc = pint(br.readLine());
		for (int test = 1; test <= tc; test++) {
			N = pint(br.readLine());
			isC=new boolean[N];
			point= new int[N][2];
			int sumx=0,sumy=0;
			for (int i = 0; i < N; i++) {
				StringTokenizer st = new StringTokenizer(br.readLine(), " ");
				point[i][0]=pint(st.nextToken());
				point[i][1]= pint(st.nextToken());
				sumx+=point[i][0];
				sumy+=point[i][1];
			}
			
			ans=Double.MAX_VALUE;
			rec(0,0, sumx, sumy);
			sb.append(ans).append("\n");
		}
		System.out.println(sb);
		
	}
	static double ans;
	static int N;
	static boolean[] isC;
	static int[][]point;
	
	static void rec(int cnt, int prev, int x, int y) {
		if(cnt==N/2) {
			ans = Math.min(ans, Math.sqrt((double)x*x + (double)y*y) );
			return;
		}
		for (int i = prev; i < N; i++)
			rec(cnt+1, i+1, x-2*point[i][0], y-2*point[i][1]);
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

[문제링크]

 

13549번: 숨바꼭질 3

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

 

비슷한 문제

-숨바꼭질

-숨바꼭질2

 

0. 수빈이의 이동 방법 3가지를 활용해, 동생에게 가장 빠르게 갈 수 있는 경우 찾기

  - 3개의 선택지가 있는 탐색 문제.

  - 어떤 선택지를 먼저 선택하느냐에 따라 해가 나오지 않거나 큰 해가 나오는 경우가 존재하므로 DFS가 아닌 BFS 사용

  - 단, 걷는 방법과 순간이동 방법의 소모 시간이 다르므로, BFS의 형식만 따를 뿐 이론적으로 BFS로 작동하지 않는다

  - ex) +1+1+1+1과 *2 *2 *2 *2는 각각 4번의 이동을 하지만, 소모 시간은 4초 / 0초로 다름

  - 전체 경우의 수를 탐색한다

 

1. 수빈이의 시작지점 N에서 BFS방식으로 +1, -1, *2 지점을 큐에 넣으며 진행한다

 

2. 수빈이가 어떤 칸 X에 W초를 걸려 도달했는데, 이미 해당 칸에 W보다 적은 시간으로 도달한 기록이 있다면, 더이상 탐색하는것은 의미가 없다.

  - 어떤 칸 X에 몇초만에 도달하였는지를 num 배열에 저장하여, 현재의 시간이 더 작을때에만 진행하도록 한다

  - 숨바꼭질 기본 문제와는 달리, 순간이동의 소모 시간이 0이므로, num[X]가 0인지 아닌지만으로는 판단할 수 없다

 

3. 수빈이의 현재 위치가 동생의 위치보다 크다면, +1 *2 연산을 진행할 필요는 없다

  - 마찬가지로, 수빈이의 현재 위치가 동생의 위치보다 작다면, -1 연산을 진행할 필요는 없다

 

4. 정리하자면,

  - 현재 좌표 X가 동생의 좌표 K보다 작으며, X+1 좌표의 이전 접근 시간이 현재 시간보다 클때만 진행+갱신

  - 현재 좌표 X가 동생의 좌표 K보다 작으며, X*2 좌표의 이전 접근 시간이 현재 시간보다 클때만 진행+갱신

  - 현재 좌표 X가 동생의 좌표 K보다 크며, X-1 좌표의 이전 접근 시간이 현재 시간보다 클때만 진행+갱신하며,

  - K좌표에 도달하는 순간 연산 횟수를 출력, 종료한다

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

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		
		int N = Integer.parseInt(st.nextToken());
		int K = Integer.parseInt(st.nextToken());
		int len=100001;
		int[] num = new int[len*2];
		int[] way = new int[len*2];
		Arrays.fill(num, Integer.MAX_VALUE);
		way[N]=1;
		num[N]=0;
		Queue<Integer> q = new LinkedList<Integer>();
		q.offer(N);
		
		while(!q.isEmpty()) {
			int temp = q.poll();
			if(temp < K && num[temp*2]>num[temp]) {
				way[temp*2]+=way[temp];
				if(num[temp*2]!=num[temp]+1)q.offer(temp*2);
				num[temp*2]=num[temp];
			}
			if(temp < K && num[temp+1]>num[temp]) {
				way[temp+1]+=way[temp];
				if(num[temp+1]!=num[temp]+1)q.offer(temp+1);
				num[temp+1]=num[temp]+1;
			}
			if(temp-1 >= 0 && num[temp-1]>num[temp]) {
				way[temp-1]+=way[temp];
				if(num[temp-1]!=num[temp]+1)q.offer(temp-1);
				num[temp-1]=num[temp]+1;
			}
		}
		System.out.println(num[K]);
	}
}

결과 화면

 

[문제링크]

 

12851번: 숨바꼭질 2

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때

www.acmicpc.net

 

비슷한 문제

-숨바꼭질

 

0. 수빈이의 이동 방법 3가지를 활용해, 동생에게 가장 빠르게 갈 수 있는 경우 + 그 갯수 찾기

  - 3개의 선택지가 있는 탐색 문제.

  - 어떤 선택지를 먼저 선택하느냐에 따라 해가 나오지 않거나 큰 해가 나오는 경우가 존재하므로 DFS가 아닌 BFS 사용

 

1. 수빈이의 시작지점 N에서 BFS방식으로 +1, -1, *2 지점을 큐에 넣으며 진행한다

 

2. 수빈이가 어떤 칸 X에 W초를 거쳐 도달했는데, 이미 해당 칸에 W보다 적은 시간으로 도달한 기록이 있다면, 더이상 탐색하는것은 의미가 없다.

  - 하지만 같은 칸에 동일하게 W초만에 도달했을 경우는 고려해야한다 (가짓수를 구하기 위해)

  - way배열을 두어 W초를 사용해 X번 칸에 도달할 수 있는 가짓수를 저장한다.

 

3. 수빈이의 현재 위치가 동생의 위치보다 크다면, 반드시 작아져야하므로 +1 *2 연산을 진행할 필요 없다

  - 마찬가지로, 수빈이의 현재 위치가 동생의 위치보다 작다면, 반드시 커져야하므로 -1 연산을 진행할 필요 없다

 

4. 정리하자면,

  - 현재 좌표 X가 동생의 좌표 K보다 작으며, X+1 좌표에 진행해본 적 없거나 || 현재 이동수 W와 같을때만 진행

  - 현재 좌표 X가 동생의 좌표 K보다 작으며, X*2 좌표에 진행해본적이 없거나 || 현재 이동수 W와 같을때만 진행

  - 현재 좌표 X가 동생의 좌표 K보다 크며, X-1 좌표에 진행해본적이 없거나 || 현재 이동수 W와 같을때만 진행하며,

  - K좌표에 도달하는 순간 연산 횟수를 출력, 종료한다

  - 시작할 때 큐의 크기 = W단계에서 연산해야할 크기이므로, 큐의 크기만큼 반복할때마다 1씩 증가하는 act 변수를 두어, 이동 수를 관리한다

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

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		
		int N = Integer.parseInt(st.nextToken());
		int K = Integer.parseInt(st.nextToken());
		int len=100001;
		int[] num = new int[len*2];
		int[] way = new int[len*2];
		way[N]=1;
		num[N]=1;
		Queue<Integer> q = new LinkedList<Integer>();
		q.offer(N);
		
		int act=1;
		while(num[K]==0) {
			act++;
			int qlen=q.size();
			for (int i = 0; i < qlen; i++) {
				int temp = q.poll();
				if(temp < K) {
					if(num[temp*2]==0) {
						q.offer(temp*2);
						num[temp*2]=act;					
					}
					if(num[temp*2]==act)way[temp*2]+=way[temp];
				}
				if(temp < K) {
					if(num[temp+1]==0) {
						q.offer(temp+1);
						num[temp+1]=act;						
					}
					if(num[temp+1]==act)way[temp+1]+=way[temp];
				}
				if(temp-1 >= 0) {
					if(num[temp-1]==0) {
						q.offer(temp-1);
						num[temp-1]=act;						
					}
					if(num[temp-1]==act)way[temp-1]+=way[temp];
				}
			}
		}
		System.out.println(num[K]-1);
		System.out.println(way[K]);
	}
}

결과 화면

[문제링크]

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

 

0. 수빈이의 이동 방법 3가지를 활용해, 동생에게 가장 빠르게 갈 수 있는 경우 찾기

  - 3개의 선택지가 있는 탐색 문제.

  - 어떤 선택지를 먼저 선택하느냐에 따라 해가 나오지 않거나 큰 해가 나오는 경우가 존재하므로 DFS가 아닌 BFS 사용

 

1. 수빈이의 시작지점 N에서 BFS방식으로 +1, -1, *2 지점을 큐에 넣으며 진행한다

 

2. 수빈이가 어떤 칸 X에 W초를 거쳐 도달했는데, 이미 해당 칸에 W보다 적은 시간으로 도달한 기록이 있다면, 더이상 탐색하는것은 의미가 없다.

  - BFS는 W초에서 갈수있는 모든 지점을 완료한 후 W+1초의 지점들에 대해 작업하므로, 값의 유/무로만 판단해도 무방하다

  - X번 노드에 도달했을 때의 이동 횟수를 num배열에 저장하여, 그 값이 없는 좌표에 대해서만 진행한다

 

3. 수빈이의 현재 위치가 동생의 위치보다 크다면, 반드시 작아져야하므로 +1 *2 연산을 진행할 필요 없다

  - 마찬가지로, 수빈이의 현재 위치가 동생의 위치보다 작다면, 반드시 커져야하므로 -1 연산을 진행할 필요 없다

 

4. 정리하자면,

  - 현재 좌표 X가 동생의 좌표 K보다 작으며, X+1 좌표에 진행해본적이 없을때 진행

  - 현재 좌표 X가 동생의 좌표 K보다 작으며, X*2 좌표에 진행해본적이 없을때 진행

  - 현재 좌표 X가 동생의 좌표 K보다 크며, X-1 좌표에 진행해본적이 없을때 진행하며,

  - K좌표에 도달하는 순간 연산 횟수를 출력, 종료한다

 

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

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		
		int N = Integer.parseInt(st.nextToken());
		int K = Integer.parseInt(st.nextToken());
		int len=100001;
		int[] num = new int[len*2];
		num[N]=1;
		Queue<Integer> q = new LinkedList<Integer>();
		q.offer(N);
		
		while(num[K]==0) {
			int temp = q.poll();
			if(temp < K && num[temp+1]==0) {
				num[temp+1]=num[temp]+1;
				q.offer(temp+1);
			}
			if(temp-1 >= 0 && num[temp-1]==0) {
				num[temp-1]=num[temp]+1;
				q.offer(temp-1);
			}
			if(temp < K && num[temp*2]==0) {
				num[temp*2]=num[temp]+1;
				q.offer(temp*2);
			}
			
		}
		System.out.println(num[K]-1);
	}
}

결과 화면

+ Recent posts