Skip to content

Instantly share code, notes, and snippets.

@skysign
Last active May 1, 2020 00:11
Show Gist options
  • Save skysign/ce93d48b1536863d40cb96437ddcdae5 to your computer and use it in GitHub Desktop.
Save skysign/ce93d48b1536863d40cb96437ddcdae5 to your computer and use it in GitHub Desktop.
4991번 로봇 청소기 / BOJ
/**
* 4991번 로봇 청소기 / BOJ
* 문제링크 : https://www.acmicpc.net/problem/4991
* 제출링크 : https://www.acmicpc.net/source/19521047
* 문제풀이 : https://skysign.tistory.com/214
*/
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
public class Main {
/** 입력값의 column갯수 */
int col;
/** 입력값의 row갯수 */
int row;
/** 입력값을 저장하고 있는 맵 */
char[][] map;
/** 청소로봇과 모든 쓰레기 사이에 거리 */
int[][] d;
/** 청소로봇과 모든 쓰레기 갯수*/
int N;
public void solve() throws IOException {
while(true) {
col = sc.nextInt();
row = sc.nextInt();
/* 문제의 정의에 따라서, 0 0 입력일 때 종료 */
if((col == row) && (col == 0))
return;
/** 청소로봇이 모든 쓰레기를 치우며, 이동하는 거리의 합중에서 최소값*/
int rMinSumOfDistance = Integer.MAX_VALUE;
map = new char[row][col];
/** 청소로봇의 시작 위치 */
Pair<Integer, Integer> robot = null;
/** 쓰레기들의 위치 */
ArrayList<Pair<Integer, Integer>> alPosTrash = new ArrayList<>();
for(int i=0; i<row; ++i) {
String line = sc.nextLine();
for(int j=0; j<col; ++j) {
char c = line.charAt(j);
map[i][j] = c;
if('o' == c)
robot = new Pair<>(i, j);
if('*' == c)
alPosTrash.add(new Pair<Integer, Integer>(i, j));
}
}
/**
* 문제에서 청소로봇이 이동하면서, 쓰레기를 치운다고 설명하고 있지만,
* 문제에서 요구하는 것을 보다, 코딩에 하기 쉽도로고 바꿔써 보면
* alPosAll(0)에서 출발해서,alPosAll(1) ~ alPosAll(N-1)까지
* 모두 방뭉하는 거리의 최소값을 찾는 문제입니다.
*/
/** 청소로봇 + 쓰레기들의 위치 */
ArrayList<Pair<Integer, Integer>> alPosAll = new ArrayList();
alPosAll.add(robot);
alPosAll.addAll(alPosTrash);
// robot 위치 + 쓰레기들의 위치, 갯수
N = alPosAll.size();
d = new int[alPosAll.size()][alPosAll.size()];
// i에서 출발해서, j에 도착하는 거리는 distance[i][j]에 저장
// i에서 추발해서, j에 도착할 수 없으면 -1 저장
// 시작은 모두 도착할 수 없다고 가정
fill2D(d, -1);
// robot의 시작 위치와, 쓰레기들의 위치를 o * 에서 인덱스로 바꿔줌
// flood fill 에서 청소로봇과, 쓰레기를 찾아서, d 에 거리를 저장하기 쉽도록 인덱스로 바꿔준다
// ASCII코드에서 0~10은 모두 제어문자임으로, 알파벳에 속하는 '.' 과 'x'등과 겹치지 않는다.
for(int i=0; i<alPosAll.size(); ++i) {
Pair<Integer, Integer> pos = alPosAll.get(i);
int xi = (Integer)pos.getKey();
int xj = (Integer)pos.getValue();
map[xi][xj] = (char)i;
}
for(int i=0; i<alPosAll.size(); ++i) {
// i 에서 출발해서, i를 제외한 다른 위치(청소로봇 or 쓰레기)까지의 거리를 구한다.
// false : 청소로봇이 이동할 수 없는 위치에 쓰레기가 있음
if(false == updateDistanceByFloodFill(alPosAll, i)) {
rMinSumOfDistance = -1;
break;
}
// flood fill을 사용해서, O(N * row * col)의 시간으로,
// 청소로봇과 쓰레기들 사이의 이동거리를 알아 낼 수 있습니다.
// 입력값의 최대값을 가정하면, O(11 * 20 * 20) 시간으로 찾을 수 있습니다.
// 완전탐색(exhaustive search)으로 거리를 찾을 수 있지만,
// 2가지 이유 때문에, 이 문제에서는 flood fill을 사용합니다.
// 1. 이 문제에서는 이동거리만 구하면 되고, 이동 경로는 필요하지 안습니다.
// 2. flood fill이 완전탐색보다 빨리 동작합니다.
}
// -1 프린트 한다음, 가장위의 while루프로 돌아감
if(-1 == rMinSumOfDistance) {
println(rMinSumOfDistance);
continue;
}
// 쓰레기들을 치우는 순서에 따라서,
// 청소로봇이 움직이는 거리가 달라진다.
// 따라서, 쓰레기의 갯수에 따라서, Permutation(순열)을 만들어,
// 순열의 순서대로, 청소로봇이 이동했을 때, 이동 경로의 합을 구한다.
// 플로이드 워셜(floyd warshall), 해밀턴 패스(halmilton path) 등의 방법으로도,
// 이 문제를 푸는 것이 가능할 것으로 생각하지만,
// 순열을 사용해서 푸는 것이 보다 간단한 풀이입니다.
// 입력에 따라서, 이동하는 방식이 2차원의 전후좌우 4가지 방향 밖에 없기 때문에,
// 쓰레기들을 방문하는 순서를 순열로만들고, 가능한 순열을 모두 만든 후에,
// 순열의 순서대로 쓰레기를 방문하여, 청소로봇의 최소이동 경로를 찾는다.
int[] idxTrashes = new int[alPosTrash.size()];
for(int i=0; i<alPosTrash.size(); ++i) {
idxTrashes[i] = i+1;
}
ArrayList<int[]> alPermutationTrash = new ArrayList<>();
permutation(idxTrashes, 0, idxTrashes.length, idxTrashes.length, alPermutationTrash);
rMinSumOfDistance = Integer.MAX_VALUE;
for(int[] per: alPermutationTrash) {
int sumOfDistance = 0;
for(int i=0; i<per.length-1; ++i) {
sumOfDistance += d[per[i]][per[i+1]];
}
sumOfDistance += d[0][per[0]];
// 이동 경로합의 최소값
rMinSumOfDistance = Math.min(rMinSumOfDistance, sumOfDistance);
}
println(rMinSumOfDistance);
}
}
/**
* i 에서 출발해서, i를 제외한 다른 위치(청소로봇 or 쓰레기)까지의 거리를 구한다.
* @param alPosAll 청소로봇과 쓰레기들의 위치
* @param idx 시작위치를 가리키는 인덱스
* @return false 이동할 수 없는 위치에 쓰레기가 있음, true 모든 쓰레기가 이동할 수 있는 위치에 있음
*/
public boolean updateDistanceByFloodFill(ArrayList<Pair<Integer, Integer>> alPosAll, int idx) {
int[][] visitied = new int[row][col];
ArrayList<Pair<Integer, Integer>> alPos = new ArrayList<>();
alPos.add(alPosAll.get(idx));
return floodFill(alPos, visitied, 1, idx);
}
public boolean floodFill(ArrayList<Pair<Integer, Integer>> alPos,
int[][] visitied, int depth, int idx) {
ArrayList<Pair<Integer, Integer>> alPosNext = new ArrayList<>();
for(Pair<Integer, Integer> pos: alPos) {
int si = (Integer) pos.getKey();
int sj = (Integer) pos.getValue();
int r = IsBeFlooded(si, sj, visitied);
if(r == 0) {
visitied[si][sj] = depth;
char m = map[si][sj];
if((0<=m) && (m<N)) {
// bidirectional 하기 때문에
d[idx][m] = depth -1;
d[m][idx] = depth -1;
}
for(int idxD4=0; idxD4<d4i.length; ++idxD4) {
int di = si + d4i[idxD4];
int dj = sj + d4j[idxD4];
alPosNext.add(new Pair<Integer, Integer>(di, dj));
}
}
}
if(0 == alPosNext.size()) {
// 이동 할 수 없는 위치에, 쓰레기가 있는지 확인
for(int j=0; j<N; ++j) {
if(-1 == d[idx][j])
return false;
}
return true;
}
return floodFill(alPosNext, visitied, depth +1, idx);
}
public int IsBeFlooded(int i, int j, int[][] visitied) {
// case : blocked / 못감 / -1
// case : can be flooded / 이동할 수 있음 / 0
boolean b;
b = ((0<= i) && (i <row));
if(!b)
return -1;
b = ((0<= j) && (j <col));
if(!b)
return -1;
if('x' == map[i][j])
return -1;
if(0 == visitied[i][j])
return 0;
// 0 != visitied[i][j] 이미 flooeded되서, 한번 방문 했던 곳
return -1;
}
// int n = ;
// for(int i=0; i<n; ++i) {
//
// }
// int n = ;
// for(int j=0; j<n; ++j) {
//
// }
// int n = ;
// for(int i=0; i<n; ++i) {
// for(int j=0; j<n; ++j) {
//
// }
// }
// int N = ;
// for(int i=0; i<n; ++i) {
// for(int j=0; j<n; ++j) {
// for(int k=0; k<n; ++k) {
//
// }
// }
// }
public void _solve() throws IOException {
solve();
bw.flush();
}
public static void main(String[] args) throws IOException {
Main main = new Main();
main._solve();
}
// begin Permutation
/*
* https://bcp0109.tistory.com/14
* depth 0
* n arr의 길이
* r 뽑는 갯수
*/
void permutation(int[] arr, int depth, int n, int r, ArrayList<int[]> al) {
if (depth == r) {
int[] rs = new int[r];
System.arraycopy(arr, 0, rs, 0, r);
al.add(rs);
return;
}
for (int i=depth; i<n; i++) {
swap(arr, depth, i);
permutation(arr, depth + 1, n, r, al);
swap(arr, depth, i);
}
}
void swap(int[] arr, int depth, int i) {
int temp = arr[depth];
arr[depth] = arr[i];
arr[i] = temp;
}
// end Permutation
public class MNode<TV> extends Node {
ArrayList<MNode<TV>> mChidren ;
MNode(TV v) {
super(v);
mChidren = new ArrayList<>();
}
}
public class BNode<TV> extends Node {
BNode<TV> mL;
BNode<TV> mR;
BNode(TV v) {
super(v);
}
}
public class Node<TV> {
public TV mV;
Node(TV v) {
set(v);
}
public void set(TV v) {
mV = v;
}
}
public int[][] pSum2D(int[][] dt) {
int[][] pSum2D = new int[dt.length][dt[0].length];
return _prefixSum2D(dt, pSum2D, dt.length, dt[0].length);
}
public int[][] pSum2D(int[][] dt, int[][] pSum2D) {
return _prefixSum2D(dt, pSum2D, dt.length, dt[0].length);
}
public int[][] _prefixSum2D(int[][] dt, int[][] pSum2D, int I, int J) {
pSum2D[0][0] = dt[0][0];
for(int j=1; j<J; ++j) {
pSum2D[0][j] = dt[0][j] + pSum2D[0][j-1];
}
for(int i=1; i<I; ++i) {
pSum2D[i][0] = dt[i][0] + pSum2D[i-1][0];
}
for(int i=1; i<I; ++i) {
for(int j=1; j<J; ++j) {
pSum2D[i][j] = dt[i][j] + pSum2D[i-1][j] + pSum2D[i][j-1] - pSum2D[i-1][j-1];
}
}
return pSum2D;
}
public int area2DbyCnt(int[][] pSum2D, int i1, int j1, int i2, int j2) {
return area2DbyIdx(pSum2D, i1-1, j1-1, i2-1, j2-1);
}
public int area2DbyIdx(int[][] pSum2D, int i1, int j1, int i2, int j2) {
int r = pSum2D[i2][j2];
if(i1>0)
r -= pSum2D[i1-1][j2];
if(j1>0)
r -= pSum2D[i2][j1-1];
if((i1>0) && (j1>0))
r += pSum2D[i1-1][j1-1];
return r;
}
public int[][] rotate90(int[][] a, int N) {
int[][] tp = new int[N][N];
for(int i=0; i<N; ++i) {
for(int j=0; j<N; ++j) {
tp[i][j] = a[N-1-j][i];
}
}
return tp;
}
public int[][] rotate180(int[][] a, int N) {
int[][] tp = new int[N][N];
for(int i=0; i<N; ++i) {
for(int j=0; j<N; ++j) {
tp[N-i-1][N-j-1] = a[i][j];
}
}
return tp;
}
public int[][] rotate270(int[][] a, int N) {
int[][] tp = new int[N][N];
for(int i=0; i<N; ++i) {
for(int j=0; j<N; ++j) {
tp[N-1-j][i] = a[i][j];
}
}
return tp;
}
public void flip_h(int[][] a, int N) {
for(int i=0; i<N; ++i) {
for(int j=0; j<N/2; ++j) {
int tp = a[i][N-j-1];
a[i][N-j-1] = a[i][j];
a[i][j] = tp;
}
}
}
public void flip_v(int[][] a, int N) {
for(int i=0; i<N/2; ++i) {
for(int j=0; j<N; ++j) {
int tp = a[N-i-1][j];
a[N-i-1][j] = a[i][j];
a[i][j] = tp;
}
}
}
public char[][] clone2D(char[][] src) {
int row = src.length;
int col = src[0].length;
char[][] dst = new char[row][col];
for (int i=0; i<row; i++) {
System.arraycopy(src[i],0, dst[i],0, col);
}
return dst;
}
public boolean[][] clone2D(boolean[][] src) {
int row = src.length;
int col = src[0].length;
boolean[][] dst = new boolean[row][col];
for (int i=0; i<row; i++) {
System.arraycopy(src[i],0, dst[i],0, col);
}
return dst;
}
public int[][] clone2D(int[][] src) {
int row = src.length;
int col = src[0].length;
int[][] dst = new int[row][col];
for (int i=0; i<row; i++) {
System.arraycopy(src[i],0, dst[i],0, col);
}
return dst;
}
public class Pair<K, V> extends _Pair implements Comparable {
public Pair(K key, V value) {
super(key, value);
}
@Override
public int compareTo(Object o) {
Pair other = (Pair)o;
int k1 = Integer.parseInt(this.getKey().toString());
int k2 = Integer.parseInt(other.getKey().toString());
if((k1 - k2) == 0) {
int v1 = Integer.parseInt(this.getValue().toString());
int v2 = Integer.parseInt(other.getValue().toString());
return v1 - v2;
}
return k1 - k2;
}
}
// http://cr.openjdk.java.net/~vadim/8140503/webrev.01/modules/base/src/main/java/javafx/util/Pair.java.html
public class _Pair<K,V> {
private K key;
public K getKey() { return key; }
public void setKey(K kk) { key = kk; }
private V value;
public V getValue() { return value; }
public void setValue(V vv) { value = vv; }
public void set(K kk, V vv) { key = kk; value = vv; }
public _Pair(K key, V value) {
this.key = key;
this.value = value;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof _Pair) {
_Pair pair = (_Pair) o;
if (key != null ? !key.equals(pair.key) : pair.key != null) return false;
if (value != null ? !value.equals(pair.value) : pair.value != null) return false;
return true;
}
return false;
}
}
// Normally used MIN/MAX number
public final int _1087654321 = 1087654321;
public final int __1087654321 = -1087654321;
public final int _987654321 = 987654321;
public final int __987654321 = -987654321;
// Commonly used numbers as a divider of mod operation to prevent overflow
public final int _10_007 = 10007;
public final int _1_000_000_007 = 1000000007;
final int MOD = _1_000_000_007;
public void print(int v) throws IOException {
print(v+"");
}
public void print(String s) throws IOException {
bw.write(s);
}
public void println() throws IOException {
print("\n");
}
public void println(int a) throws IOException {
print(a+"\n");
}
public void println(long a) throws IOException {
print(a+"\n");
}
public void println(double a) throws IOException {
print(a+"\n");
}
public void println(String s) throws IOException {
bw.write(s+"\n");
}
// -1
public final int MINUS_1 = -1;
// 4 ways
public int[] d4i = new int[]{-1, 1, 0, 0};
public int[] d4j = new int[]{0, 0, -1, 1};
// 8 ways
public int[] d8i = new int[]{-1, 1, 0, 0, -1, -1, 1, 1};
public int[] d8j = new int[]{0, 0, -1, 1, -1, 1, 1, -1};
// Initialize 2D arrays with value v
public void fill2D(int[][] _2D, int v) {
for(int[] _1D: _2D) {
Arrays.fill(_1D, v);
}
}
public void fill2D(long[][] _2D, long v) {
for(long[] _1D: _2D) {
Arrays.fill(_1D, v);
}
}
public void fill2D(double[][] _2D, double v) {
for(double[] _1D: _2D) {
Arrays.fill(_1D, v);
}
}
public void print2D(int[][] dp) throws IOException {
print(" ");
for(int j=0; j<dp[0].length; ++j){
print(SS(j)+j + " ");
}
println();
for(int i=0; i<dp.length; ++i){
print(SS(i)+i+"|");
for(int j=0; j<dp[0].length; ++j){
print(SS(dp[i][j])+dp[i][j] + " ");
}
println();
}
}
public void print2D(long[][] dp) throws IOException {
print(" ");
for(int j=0; j<dp[0].length; ++j){
print(SS(j)+j + " ");
}
println();
for(int i=0; i<dp.length; ++i){
print(SS(i)+i+"|");
for(int j=0; j<dp[0].length; ++j){
print(SS(dp[i][j])+dp[i][j] + " ");
}
println();
}
}
public final int LENGHT_OF_NUMBER = 3;
/**
* Generate space string for alignment when we print 2D arrays.
* its length is LENGHT_OF_NUMBER - the length of number
* Basically, we assume that number a is less than 1000.
* So, LENGHT_OF_NUMBER is 3 by default.
* @param a
* @return
*/
public String SS(int a) {
String s = a+"";
int l = LENGHT_OF_NUMBER - s.length();
StringBuilder sb = new StringBuilder();
for(int i=0; i<l; ++i) {
sb.append(" ");
}
return sb.toString();
}
public String SS(long a) {
String s = a+"";
int l = LENGHT_OF_NUMBER - s.length();
StringBuilder sb = new StringBuilder();
for(int i=0; i<l; ++i) {
sb.append(" ");
}
return sb.toString();
}
// Initialize 3D arrays with value v
public void fill3D(int[][][] _3D, int v) {
for(int[][] _2D: _3D) {
for(int[] _1D: _2D) {
Arrays.fill(_1D, v);
}
}
}
// GCD
int GCD(int a, int b) {
if(0 == b) {
return a;
}
return GCD(b, a%b);
}
long GCD(long a, long b) {
if(0 == b) {
return a;
}
return GCD(b, a%b);
}
boolean IsEven(int a) {
return (a>>1) == ((a>>1)<<1);
}
boolean IsOdd(int a) {
return (a>>1) != ((a>>1)<<1);
}
boolean IsEven(long a) {
return (a>>1) == ((a>>1)<<1);
}
boolean IsOdd(long a) {
return (a>>1) != ((a>>1)<<1);
}
public Reader sc = new Reader();
// Sometimes, Reader class cause unknown problem, when I submit my java code to judge server.
// For more detail, please see https://algospot.com/forum/read/4731/
// public Scanner sc = new Scanner(System.in);
public BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
// https://www.geeksforgeeks.org/fast-io-in-java-in-competitive-programming/
public class Reader
{
final private int BUFFER_SIZE = 1 << 16;
private DataInputStream din;
private byte[] buffer;
private int bufferPointer, bytesRead;
public Reader()
{
din = new DataInputStream(System.in);
buffer = new byte[BUFFER_SIZE];
bufferPointer = bytesRead = 0;
}
public Reader(String file_name) throws IOException
{
din = new DataInputStream(new FileInputStream(file_name));
buffer = new byte[BUFFER_SIZE];
bufferPointer = bytesRead = 0;
}
public String nextLine() throws IOException
{
byte[] buf = new byte[64]; // line length
int cnt = 0, c;
while ((c = read()) != -1)
{
if (c == '\n')
break;
buf[cnt++] = (byte) c;
}
return new String(buf, 0, cnt);
}
public int nextInt() throws IOException
{
int ret = 0;
byte c = read();
while (c <= ' ')
c = read();
boolean neg = (c == '-');
if (neg)
c = read();
do
{
ret = ret * 10 + c - '0';
} while ((c = read()) >= '0' && c <= '9');
if (neg)
return -ret;
return ret;
}
public long nextLong() throws IOException
{
long ret = 0;
byte c = read();
while (c <= ' ')
c = read();
boolean neg = (c == '-');
if (neg)
c = read();
do {
ret = ret * 10 + c - '0';
}
while ((c = read()) >= '0' && c <= '9');
if (neg)
return -ret;
return ret;
}
public double nextDouble() throws IOException
{
double ret = 0, div = 1;
byte c = read();
while (c <= ' ')
c = read();
boolean neg = (c == '-');
if (neg)
c = read();
do {
ret = ret * 10 + c - '0';
}
while ((c = read()) >= '0' && c <= '9');
if (c == '.')
{
while ((c = read()) >= '0' && c <= '9')
{
ret += (c - '0') / (div *= 10);
}
}
if (neg)
return -ret;
return ret;
}
private void fillBuffer() throws IOException
{
bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
if (bytesRead == -1)
buffer[0] = -1;
}
private byte read() throws IOException
{
if (bufferPointer == bytesRead)
fillBuffer();
return buffer[bufferPointer++];
}
public void close() throws IOException
{
if (din == null)
return;
din.close();
}
}
// public void test_matrix_methods() throws IOException {
// // Test Case 1
// int m1[][] = { {1, 2, 3, 4},
// {5, 6, 7, 8},
// {9, 10, 11, 12},
// {13, 14, 15, 16} };
//
// // Tese Case 2
// int m2[][] = { {1, 2, 3},
// {4, 5, 6},
// {7, 8, 9} };
//
// println("Matrix Original");
// print2D(m1);
//
// println("Matrix Rotate 90");
// print2D(rotate90(m1, m1.length));
//
// println("Matrix Rotate 180");
// print2D(rotate180(m1, m1.length));
//
// println("Matrix Rotate 270");
// print2D(rotate270(m1, m1.length));
//
// println("Matrix Flip Horizontal");
// int[][] m1h = clone2D(m1);
// flip_h(m1h, m1h.length);
// print2D(m1h);
//
// println("Matrix Flip Vertical");
// int[][] m1v = clone2D(m1);
// flip_v(m1v, m1v.length);
// print2D(m1v);
//
// println();
//
// println("Matrix Original");
// print2D(m2);
//
// println("Matrix Rotate 90");
// print2D(rotate90(m2, m2.length));
//
// println("Matrix Rotate 180");
// print2D(rotate180(m2, m2.length));
//
// println("Matrix Rotate 270");
// print2D(rotate270(m2, m2.length));
//
// println("Matrix Flip Horizontal");
// int[][] m2h = clone2D(m2);
// flip_h(m2h, m2h.length);
// print2D(m2h);
//
// println("Matrix Flip Vertical");
// int[][] m2v = clone2D(m2);
// flip_v(m2v, m2v.length);
// print2D(m2v);
// }
//
// public void test_pSum2D_methods() throws IOException {
// // Test Case 1
// int m1[][] = { {1, 1, 1, 1},
// {1, 1, 1, 1},
// {1, 1, 1, 1},
// {1, 1, 1, 1} };
//
// println("Matrix Original");
// print2D(m1);
//
// int[][] m1pSum2D = new int[m1.length][m1[0].length];
// println("pSum2D");
// pSum2D(m1, m1pSum2D);
// print2D(m1pSum2D);
//
// println(area2DbyIdx(m1pSum2D, 1, 1, 3, 3));
// println(area2DbyIdx(m1pSum2D, 0, 0, 3, 3));
// println(area2DbyIdx(m1pSum2D, 0, 1, 3, 3));
// println(area2DbyIdx(m1pSum2D, 1, 0, 3, 3));
// }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment