[java]연구소 3 - 백준17142
문제
풀이
문제 해석을 하자면,
- 문제 조건에 맞춰서 바이러스가 “전부 장악한” 시점의 시간을 구하는 문제이며,
- 이 시간의 최소시간을 구하는 문제이다.
풀이
- 두가지로 나눠서 구현을 진행했다.
- 먼저, “조합” 의 경우의 수로 접근 -> 따로 virusCount 로 바이러스 총 개수 구해서 사용 중요!!
- 이후 BFS !! -> “빈값” 일때만 time 기록!!! (거리기록!!) 매우 중요 포인트
- dist 배열로 거리를 측정해본적 있다면, 그 방식과 유사
- 아래 두가지 반례를 해결하기 위한 목적도 있으니 참고
- 그리고 구현의 편의를 위해서 아래 사항을 진행했다 -> 아래 최대값시간 도출 위해
- 벽 1이 아닌 -1로 따로 표시
- 바이러스도 2가 아닌 -2로 따로 표시
- 마지막으로 전체 바이러스 장악 성공여부 확인 + dist(copy)배열 시간 최대값(=최종완료시간) 도출
아래 두가지 반례
5 1
2 2 2 1 1
2 1 1 1 1
2 1 1 1 1
2 1 1 1 1
2 2 2 1 1
0
9 1
0 2 2 2 2 2 2 2 0
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
4
코드
public static int N, M;
public static int virusCount;
public static int result=Integer.MAX_VALUE;
public static int[][] inArr = new int[55][55];
public static boolean[][] visited = new boolean[55][55];
public static int[] outArr = new int[15];
public static Pair[] virus = new Pair[15];
public static int[] dx = {0,1,0,-1};
public static int[] dy = {1,0,-1,0};
public static void bfs() {
//inArr 복제
int[][] copy = new int[55][55]; // -> dist 역할과 동일
//init
for(int i=0; i<N; i++) {
for(int j=0; j<N; j++) {
visited[i][j] = false;
copy[i][j] = inArr[i][j];
}
}
Queue<Pair> qu = new LinkedList<>();
for(int i=0; i<M; i++) {
qu.add(virus[outArr[i]]);
visited[virus[outArr[i]].x][virus[outArr[i]].y]=true;
}
int time = 0;
while(!qu.isEmpty()) {
int sizeQ = qu.size();
time++;
for(int s=0; s<sizeQ; s++) {
Pair p = qu.peek();
qu.remove();
for(int i=0; i<4; i++) {
int nx = p.x+dx[i];
int ny = p.y+dy[i];
if(nx<0||ny<0||nx>=N||ny>=N) continue;
if(visited[nx][ny] || copy[nx][ny]==-1) continue;
visited[nx][ny] = true; // 방문은 일단 표시
if(copy[nx][ny]==-2) {
qu.add(new Pair(nx, ny));
continue;
}else {
copy[nx][ny]=time; // "빈값" 일때만 time 기록!!! (거리기록!!) 매우 중요 포인트
qu.add(new Pair(nx, ny));
}
}
}
}
// 전체 바이러스 장악 성공여부 확인 + dist(copy)배열 시간 최대값(=최종완료시간) 도출
boolean fail = false;
int max = 0;
for(int i=0; i<N; i++) {
for(int j=0; j<N; j++) {
if(copy[i][j]==0) fail=true;
if(max<copy[i][j]) max = copy[i][j]; // 최대값이 전부 바이러스 전파 완료한 시간
}
}
if(!fail) result = Math.min(result, max);
//debug
// for(int i=0; i<N; i++) System.out.println(Arrays.toString(copy[i]));
// for(int i=0; i<M; i++) System.out.print(outArr[i]+" ");
// System.out.println("virus 사용");
}
public static void dfs(int depth, int idx) {
//base condition
if(depth == M) {
bfs();
return;
}
//recursion
for(int i=idx; i<virusCount; i++) {
outArr[depth] = i; // virus의 index를 기록
dfs(depth+1, i+1);
}
}
public static void main(String[] args) throws Exception {
//init
//input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
N = Integer.parseInt(stk.nextToken());
M = Integer.parseInt(stk.nextToken());
virusCount = 0;
for(int i=0; i<N; i++) {
stk = new StringTokenizer(br.readLine(), " ");
for(int j=0; j<N; j++) {
inArr[i][j] = Integer.parseInt(stk.nextToken());
if(inArr[i][j]==1) inArr[i][j]=-1; // 벽 -1로 따로 표시하겠음
if(inArr[i][j] == 2) {
virus[virusCount++] = new Pair(i,j);
inArr[i][j] = -2; // 바이러스도 보기편하게 -2로 변경
}
}
}
//run
//debug
// for(int i=0; i<N; i++) System.out.println(Arrays.toString(inArr[i]));
// System.out.println("시작전");
dfs(0, 0);
//output
if(result == Integer.MAX_VALUE) result = -1; // 바이러스 장악 실패시 -1
System.out.println(result);
}
static class Pair{
int x; int y;
public Pair(int x, int y) {
this.x = x; this.y = y;
}
}
느낀점
“빈값” 일때만 time(거리)을 기록 했으면 수많은 반례를 해결할 수 있었는데, 이를 너무 늦게 알았던 문제
댓글남기기