[문제링크]

 

1504번: 특정한 최단 경로

첫째 줄에 정점의 개수 N과 간선의 개수 E가 주어진다. (2 ≤ N ≤ 800, 0 ≤ E ≤ 200,000) 둘째 줄부터 E개의 줄에 걸쳐서 세 개의 정수 a, b, c가 주어지는데, a번 정점에서 b번 정점까지 양방향 길이 존

www.acmicpc.net

0. 1번 노드에서 v1,v2를 거쳐 N번 노드로 이동하는 방법은 2가지이다

  - v1을 먼저 방문 : 1-v1-v2-N

  - v2를 먼저 방문 : 1-v2-v1-N

  - 조건상 두 지점간 거리의 최댓값은 800개의 노드 가 1000 길이의 길로 연결됬을 경우인 799K이다

 

1. 1번노드, v1번 노드, v2번 노드에서 각각 다익스트라를 돌려, 다른 노드로 가는 최솟값을 찾는다

 

2. 다익스트라 계산 후 1-v1-v2-N 과 1-v2-v1-N 두 방법 중 cost가 작은것을 찾는다

 

3. 이론상 최대치가 약 80만이므로, 1-v1 / v1-v2 / v2-N 3번의 이동이 다 최대 cost에 가깝다고 해도 총 cost는 240만보다 작다

  - cost가 300만 이상의 결과가 나왔다면 경로가 존재하지 않아 초깃값이 계산된 경우이므로 -1을 출력한다

  - 그 외라면 구한 cost를 출력한다

 

 

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());

		int[][]graph = new int[n+1][n+1];
		
		for (int i = 0; i < m; i++) {
			st=new StringTokenizer(br.readLine()," ");
			int n1 = pint(st.nextToken());
			int n2 = pint(st.nextToken());
			int c = pint(st.nextToken());
			graph[n1][n2]=c;
			graph[n2][n1]=c;
		}
		st=new StringTokenizer(br.readLine()," ");
		int n1 = pint(st.nextToken());
		int n2 = pint(st.nextToken());
	
		//다익스트라 준비물 * 3
		int[]dilk1=new int[n+1];
		int[]dilkn1=new int[n+1];
		int[]dilkn2=new int[n+1];
		Arrays.fill(dilk1, 3000000);
		Arrays.fill(dilkn1, 3000000);
		Arrays.fill(dilkn2, 3000000);
		
		boolean[] isV=new boolean[n+1];
		boolean[] isVn1=new boolean[n+1];
		boolean[] isVn2=new boolean[n+1];
		dilk1[1]=0;
		dilkn1[n1]=0;
		dilkn2[n2]=0;
		
		for (int i = 0; i < n; i++) {
			//아직 방문 안한 노드 중 가장 가까운 노드 탐색
			int min=Integer.MAX_VALUE, minnode=0;
			int minn1=Integer.MAX_VALUE, minnoden1=0;
			int minn2=Integer.MAX_VALUE, minnoden2=0;
			for (int j = 1; j <= n; j++) {
				if(!isV[j] && dilk1[j]<min) {
					min=dilk1[j];
					minnode=j;
				}if(!isVn1[j] && dilkn1[j]<minn1) {
					minn1=dilkn1[j];
					minnoden1=j;
				}if(!isVn2[j] && dilkn2[j]<minn2) {
					minn2=dilkn2[j];
					minnoden2=j;
				}
			}
			//해당 노드 방문처리
			isV[minnode]=true;
			isVn1[minnoden1]=true;
			isVn2[minnoden2]=true;
			
			for (int j = 1; j <= n; j++) {
				//아직 방문 안한 노드에 대해 cost 업데이트
				if(!isV[j] && graph[minnode][j]!=0) {
					dilk1[j]=Math.min(dilk1[j], dilk1[minnode]+graph[minnode][j]);
				}
				if(!isVn1[j] && graph[minnoden1][j]!=0) {
					dilkn1[j]=Math.min(dilkn1[j], dilkn1[minnoden1]+graph[minnoden1][j]);
				}
				if(!isVn2[j] && graph[minnoden2][j]!=0) {
					dilkn2[j]=Math.min(dilkn2[j], dilkn2[minnoden2]+graph[minnoden2][j]);
				}
			}
			
		}
		
		//1->n1->n2->n
		int cost=3000000;
		
		cost = Math.min(dilk1[n1]+dilkn1[n2]+dilkn2[n], dilk1[n2]+dilkn2[n1]+dilkn1[n]);
		if(cost>=3000000)System.out.println(-1);
		else System.out.println(cost);
		
	}
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

'알고리즘 문제 풀이 > BOJ 골드' 카테고리의 다른 글

[백준] 15686 - 치킨 배달  (0) 2021.04.15
[백준] 9935 - 문자열 폭발  (0) 2021.04.14
[백준] 1019 - 책 페이지  (0) 2021.04.13
[백준] 1566 - P배열  (0) 2021.04.13
[백준] 12865 - 평범한 배낭  (0) 2021.04.12
[백준] 17136 - 색종이 붙이기  (0) 2021.04.12
[백준] 2629 - 양팔저울  (0) 2021.04.12

[문제링크]

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 

0. 모든 경우의 수를 탐색 - BFS

  - 같은 공간 큰 cost를 가지고 접근할 경우 더이상 진행하지 않으므로, DFS보다 수행횟수가 적다

  - 외각을 -1로 감싸 나가지 못하도록 처리

 

1. (1, 1) 좌표부터 bfs 진행 후 N,N좌표의 cost가 최소 cost

 

개선점

1. 현재 bfs는 2-3-4순으로 진입시 3,4에 대해선 진행하지 않지만, 4-3-2순으로 진입시 4,3,2 전부 실행하게된다.

  - priority queue를 이용해 해결 가능

 

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

public class Solution{
	static int[] dx = {1,0,-1,0};
	static int[] dy = {0,1,0,-1};
	
	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 testcase = 1; testcase <= tc; testcase++) {
			int N = pint(br.readLine());
			int[][] map = new int[N+2][N+2];
			int[][] cost = new int[N+2][N+2];
			
			//외곽설정, cost초기화
			for (int i = 0; i < N+2; i++) {
				map[0][i]=-1;map[N+1][i]=-1;
				map[i][0]=-1;map[i][N+1]=-1;
				Arrays.fill(cost[i], Integer.MAX_VALUE);
			}
			
			//입력받기
			for (int i = 0; i < N; i++) {
				String s = br.readLine();
				for (int j = 0; j < N; j++) {
					map[i+1][j+1]=s.charAt(j)-'0';
				}
			}
			
			Queue<int[]> qu = new LinkedList<>();
			qu.offer(new int[] {1,1});
			cost[1][1]=0;
			
			while(!qu.isEmpty()) {
				int[]cur = qu.poll();
				int curCost = cost[cur[0]][cur[1]];
				
				//더 작은 코스트로 진행할 수 있다면
				for (int i = 0; i < 4; i++) {
					int tx=cur[0]+dx[i], ty=cur[1]+dy[i];
					
					if(map[ tx ][ ty ]!=-1 && curCost + map[ tx ][ ty ] < cost[ tx ][ ty ]) {
						qu.offer(new int[] {tx, ty});
						cost[ tx ][ ty ]=curCost+map[ tx ][ ty ];
					}
				}
			}
			sb.append("#").append(testcase).append(" ").append(cost[N][N]).append("\n");
		}
		System.out.println(sb);
	}
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

[문제링크]

 

17136번: 색종이 붙이기

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. <그림 1> 색종이를 크

www.acmicpc.net

 

0. 큰 색종이부터 차례대로, 모든 붙일수 있는 부분에 붙여보고 떼어보며 진행하는 brute-force 문제

chk함수 : n*n 공간이 전부 1이면, 색종이를 붙일 수 있으면 true를 반환

remove : n*n 색종이를 붙이는 함수, 모든 1을 0으로 전환

repair : n*n 색종이를 떼는 함수, 모든 0을 1로 전환

 

1. 10*10 색종이의 모든 가능한 n*n 공간을 검사하며 색종이를 붙여보고, 더이상 붙일 수 없다면 n-1번째 색종이로 넘어가서 붙여보기를 시도한다.

 

2. 가지치기 1 : 앞으로 사용 가능한 모든 색종이를 사용해도 현재 남아있는 1의 갯수를 다 덮을만큼 충분하지 않다면, 포기하고 돌아간다.

 

3. 가지치기 2 : 이전 연산에서 구했던 해답보다 더 많은 색종이를 이미 사용했다면, 포기하고 돌아간다

 

4. min은 최대 25이므로, 그 이상의 값을 초깃값으로 지정한 후 갱신되지 않았다면 -1을 출력, 아니면 갱신된 값을 출력한다.

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));
		
		map=new int[10][10];
		int num=0;
		for (int i = 0; i < 10; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine(), " ");
			for (int j = 0; j < 10; j++) {
				map[i][j]=Integer.parseInt(st.nextToken());
				if(map[i][j]==1)num++;
			}
		}
		
		min=Integer.MAX_VALUE;
		search(0,0,0,5, num);
		System.out.println(min==Integer.MAX_VALUE?-1:min);
	}
	static int min;
	static int[][]map;
	static int limit[]=new int[] {0,5,5,5,5,5};
	static int[] maximunArea= {0,0,5,20,45,80};
	
	static void search(int x, int y, int cnt, int k, int num) {
    
		//가지치기
		//앞으로 쓸수잇는 모든 색종이를 다 써도 모든 1을 덮을수 없다면, 돌아간다
		if(maximunArea[k]+k*k*limit[k] < num) {
			return;
		}
		//다른 정답에서 구한 min값보다 이미 많은 수의 색종이를 썼다면, 돌아간다
		if(cnt>=min)return;
		//1을 다 붙였으면 min 갱신 후, 돌아간다
		if(num==0) {
			min=min<cnt?min:cnt;
			return;
		}
		//다 붙이는데 실패했다면, 돌아간다.
		if(k==0)return;
		
        
		
		for (int i = x; i < 11-k; i++) {
			for (int j = 0; j < 11-k; j++) {
				if(map[i][j]==1 && limit[k]>0) {
					if(chk(i,j,k)) {
						//제거
						remove(i,j,k);
						limit[k]--;
						//그 위치부터 다시 재귀
						
						search(i,j,cnt+1,k, num-k*k);
						
						//복구
						repair(i,j,k);
						limit[k]++;
					}
				}
			}
		}
		
		
		search(0,0,cnt,k-1, num);
		
	}
	
	static boolean chk(int x, int y, int k) {
		for (int i = x; i < x+k; i++) {
			for (int j = y; j < y+k; j++) {
				if(map[i][j]==0)return false;
			}
		}
		return true;
	}
	static void remove(int x, int y, int k) {
		for (int i = x; i < x+k; i++) {
			for (int j = y; j < y+k; j++) {
				map[i][j]=0;
			}
		}
	}
	static void repair(int x, int y, int k) {
		for (int i = x; i < x+k; i++) {
			for (int j = y; j < y+k; j++) {
				map[i][j]=1;
			}
		}
	}
	
}

결과 화면

'알고리즘 문제 풀이 > BOJ 골드' 카테고리의 다른 글

[백준] 15686 - 치킨 배달  (0) 2021.04.15
[백준] 9935 - 문자열 폭발  (0) 2021.04.14
[백준] 1019 - 책 페이지  (0) 2021.04.13
[백준] 1566 - P배열  (0) 2021.04.13
[백준] 12865 - 평범한 배낭  (0) 2021.04.12
[백준] 1504 - 특정한 최단 경로  (0) 2021.04.12
[백준] 2629 - 양팔저울  (0) 2021.04.12

[문제링크]

 

2629번: 양팔저울

첫째 줄에는 추의 개수가 자연수로 주어진다. 추의 개수는 30 이하이다. 둘째 줄에는 추의 무게들이 자연수로 가벼운 것부터 차례로 주어진다. 같은 무게의 추가 여러 개 있을 수도 있다. 추의 무

www.acmicpc.net

 

0. 최대 무게 500g인 추가 최대 30개 들어오므로, 최대 탐색 가능 범위는 -15000 ~ 15000

 

1. 추를 올리지 않는 경우, 물건의 반대편에 올리는 경우(+), 물건쪽에 올리는 경우(-) 3가지 경우를

모든 추에 대해 재귀로 진행하며, 이 과정에서 거치는 모든 무게를 true로 표시한다.

  - 이때, 추를 추가한 무게가 앞 재귀에서 나왔던 무게일 경우, 중복 연산이 발생하므로 진입하지 않는다 (가지치기) 

 

2. 물건의 무게를 입력받고, 15000초과의 무게일 경우 즉시 N을, 그 이하일 경우 true/false 값에 따라 Y/N을 출력해준다.

 

추가적으로 생각해본 개선점

1. dfs처럼 재귀를 돌며 깊은 단계에 진입 때문에, 깊은 단계에서 possible 배열이 수정되어도

  얕은 단계에 영향을 주지 못하도록 possible배열이 2중으로 구현되어야 했음.

  - bfs처럼 큐에 넣어가며 단계별로 작업했다면 possible 배열 하나로 가능하지 않았을까?

 

2. -7500 값은 적어도 500g추 15개가 있어야 도달 가능한 값이므로,

  나머지 추 15개를 전부 +로 설정해도 0이상의 값을 가질 수 없다.

  - -7500 ~ 15000으로 possible 배열을 사용하면 크기를 25%정도 절약할 수 있었음.

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

public class Main {
	static int num;
	static int[] weight;
	static boolean[][] possible;
    
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuffer sb = new StringBuffer();
		
		num=Integer.parseInt(br.readLine());
		weight = new int[num];
		
		//추 무게 입력
		StringTokenizer st = new StringTokenizer(br.readLine());
		for (int i = 0; i < num; i++) {
			weight[i]=Integer.parseInt(st.nextToken());
		}
		
		//해당무게의 표현 가능 여부 표시를 위한 boolean배열
		//추는 500g이하, 30개이하이므로, 최대 +15000, 최소 -15000까지 탐색할 수 있다
		//15000 지점을 가상의 0으로 가정, 30001개의 배열로 선언한다.
		possible = new boolean[30001][num];
		
		//추에 대한 선택지는 3가지 - 올리지 않는다|구슬쪽에 올린다|반대편에 올린다
		//3가지 선택지를 탐색하며 모든 계산 가능한 경우를 true로 마크한다
		search(0, 15000);//초깃값은 가상의 0인 15000

		//구슬의 개수 입력받기
		int ball_num = Integer.parseInt(br.readLine());
		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < ball_num; i++) {
			int ball_weight = Integer.parseInt(st.nextToken());
			//최대 무게인 15000을 넘으면 바로 N
			if(ball_weight>15000) {
				sb.append("N ");
			}
			//해당 무게가 가능하다면 Y, 아니면 N을 기록한다.
			else sb.append(possible[ball_weight+15000][num-1]?"Y ":"N ");
		}
		System.out.println(sb);
	}
    
	static void search(int cnt, int cur) {
		//모든 추에 대해 탐색 종료
		if(cnt==num) {
			return;
		}
		else {
			//해당 무게에 도달한 적 없다면 진입한다
			if(!possible[ cur+weight[cnt] ][cnt]) {
				possible[ cur+weight[cnt] ][cnt]=true;
				search(cnt+1, cur+weight[cnt]);
			}
			//해당 무게에 도달한 적 없다면 진입한다
			if(!possible[ cur-weight[cnt] ][cnt]) {
				possible[ cur-weight[cnt] ][cnt]=true;
				search(cnt+1, cur-weight[cnt]);
			}
			//올리지 않는다
			possible[cur][cnt]=true;
			search(cnt+1, cur);
		}
	}
}

결과 화면

 

[문제링크]

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 

0. 문제는 4사분면같은 x,y좌표를 사용하지만 (+x방향이 오른쪽, +y방향이 아랫쪽)

2차원배열의 인덱스는 아랫쪽-오른쪽의 방향으로 증가하므로, 입력받는 단계에서 x, y를 뒤집어 입력받았다.

 

1. 모든 BC에 대해 A, B 각각 충전 가능 여부를 판단한다

 

2. A 혹은 B 한쪽에만 속하는 BC들의 충전값중 최댓값을 구한다

 

3. A와 B 양쪽에 속하는 BC들의 충전값 중 더 좋은 선택이 있는 경우, A와 B중 작은 충전값을 갱신한다

   - A와 B 충전량의 합계 중 최댓값을 구하는 목적이므로, 동일 BC 접근시 충전량 분배는 고려할 필요 없다

   - A와 B가 300짜리 BC를 공유해 150/150이 되나, A가 독점해 300/0이 되나 합계는 300으로 동일하기 때문이다

 

4. 구해진 A와 B의 최댓값을 누적시켜가며 반복한다

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

public class Solution{
	static int dir[][]=new int[][] {
		{0,0},{-1,0},{0,1},{1,0},{0,-1}
	};
    
	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 testcase = 1; testcase <= tc; testcase++) {
			StringTokenizer st = new StringTokenizer(br.readLine(), " ");
			int n = pint(st.nextToken());
			int m = pint(st.nextToken());
			
			int[] A = new int[] {0,0};
			int[] B = new int[] {9,9};
			
			int[] moveA = new int[n];
			int[] moveB = new int[n];
			
			st = new StringTokenizer(br.readLine(), " ");
			for (int i = 0; i < n; i++)	moveA[i]=pint(st.nextToken());
			
			st = new StringTokenizer(br.readLine(), " ");
			for (int i = 0; i < n; i++)	moveB[i]=pint(st.nextToken());
			
			int[][] station = new int[m][4];
			for (int i = 0; i < m; i++) {
				st = new StringTokenizer(br.readLine(), " ");
                //x, y swap
				station[i][1]=pint(st.nextToken())-1;
				station[i][0]=pint(st.nextToken())-1;
				station[i][2]=pint(st.nextToken());
				station[i][3]=pint(st.nextToken());
			}
			
			int total=0;
			
			//n초 실행
			for (int i = 0; i <= n; i++) {
				int[][] po = new int[m][2];
				//각 지점별 A,B의 충전가능여부 확인
				for (int j = 0; j < m; j++) {
					if(Math.abs(A[0]-station[j][0])+Math.abs(A[1]-station[j][1]) <= station[j][2] )
						po[j][0]++;
					if(Math.abs(B[0]-station[j][0])+Math.abs(B[1]-station[j][1]) <= station[j][2] )
						po[j][1]++;
				}
				//1인 것들 중 max값 구하기
				int maxA=0, maxB=0;
				for (int j = 0; j < m; j++) {
					if(po[j][0]==1 && po[j][1]==0 && station[j][3]>maxA) {
						maxA=station[j][3];
					}
					if(po[j][0]==0 && po[j][1]==1 && station[j][3]>maxB) {
						maxB=station[j][3];
					}
				}
				
				
				//2인게 존재하고, maxA나 maxB보다 크다면 교체
				for (int j = 0; j < m; j++) {
					if(po[j][1]==1 && po[j][0]==1) {
						if(maxA<=maxB && maxA<station[j][3]) {
							maxA=station[j][3];
						}
						else if(maxB<=maxA && maxB<station[j][3]) {
							maxB=station[j][3];
						}
					}
				}
				
				total+=maxA+maxB;
				
				if(i==n)break;
				else {
					A[0]+=dir[moveA[i]][0];	A[1]+=dir[moveA[i]][1];
					B[0]+=dir[moveB[i]][0];	B[1]+=dir[moveB[i]][1];
				}
			}
			sb.append("#").append(testcase).append(" ").append(total).append("\n");
		}
		System.out.println(sb);
	}
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

 

채점 결과

 

+ Recent posts