문제

눈금 간격이 MxN인 크기의 모눈종이 위에 눈금에 맞춰 K개의 직사각형을 그릴 때, K개의 직사각형의 내부를 제외한 나머지 부분이 몇 개의 분리된 영역으로 나누어지는지 구하고, 각각 영역의 크기를 오름차순으로 출력하는 문제.

※ 예제 : (0,2) (4,4) , (1,1) (2,5) , (4,0) (6,2) 로 입력을 받았음.



접근방법

  1. 주어진 모눈종이는 보통 사용하는 이차원 배열과는 조금 다르게 생겼다. 이차원배열로 구현하기 위해 조금 바꾸어준다.

    1) 왼쪽 위가 (0,0)이 되도록 모눈종이를 뒤집어도, 결과 영역은 달라지는게 없으므로, 원래 배열과 같이 (0,0)을 가장 위로 생각하고 입력을 받는다.

    2) 이차원배열과 달리 모눈종이의 '점'을 입력으로 받기 때문에, 이를 배열의 '칸'으로 바꿔주어야 한다.

    • 각 점이 아니라, 각 칸을 기준으로 좌표를 계산하면 처음 입력했던 (0,2)는 (2,0)에 들어가며, (4,4)는 (3,3)에 들어간다. 즉, 마지막 점을 배열의 칸으로 변경하면 해당 점의 왼쪽 위 좌표와 동일하므로, 행, 열에서 1씩 빼준 값을 넣어주면 된다.

  2. 영역의 개수를 구하는 부분은 BFS를 이용한다.

  3. 영역별 크기를 구하기 위해서는 BFS 함수를 실행할 때 size를 체크해준다. 즉, 큐에 원소를 넣을 때 마다 size를 증가시켜준다.

  4. 영역별 크기를 오름차순으로 출력하기 위해서 Collection을 이용한다. ArrayList를 정렬하는 코드 : Collections.sort(sizeList);



소스코드

public class Main {

   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x = x; this.y = y;}
  }

   static int[] dx = {1, -1, 0, 0};
   static int[] dy = {0, 0, 1, -1};
   static int result = 0;
   static int N, M, K;
   static boolean[][] visited;
   static int[][] map;
   static Queue<Pos> queue;
   static ArrayList<Integer> sizeList;

   static void BFS(){
       int size = 1;
       while(!queue.isEmpty()){
           Pos cur_pos = queue.poll();

           for(int i=0; i<4; i++){
               Pos next_pos = new Pos(cur_pos.x + dx[i], cur_pos.y + dy[i]);
               if(map[next_pos.x][next_pos.y] != 1 && !visited[next_pos.x][next_pos.y]){
                   visited[next_pos.x][next_pos.y] = true;
                   queue.offer(next_pos);
                   ++size;
              }
          }
      }
       sizeList.add(size);
  }

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

       String input[] = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       M = Integer.parseInt(input[1]);
       K = Integer.parseInt(input[2]);

       visited = new boolean[N+2][M+2];
       map = new int[N+2][M+2];
       
       // 벽 설정(배열범위 초과 방지)
       for(int i=0; i<=N+1; i++)
           for(int j=0; j<=M+1; j++)
               if(i==0 || i==N+1 || j==0 || j==M+1)
                   map[i][j] = 1;

       // 색칠된 부분 1로 변경
       for (int i = 0; i < K; i++) {
           String[] pos = in.readLine().split(" ");
           int start_y = Integer.parseInt(pos[0]);
           int start_x = Integer.parseInt(pos[1]);
           int end_y = Integer.parseInt(pos[2])-1;
           int end_x = Integer.parseInt(pos[3])-1;

           for (int r = start_x; r <= end_x; r++)
               for (int c = start_y; c <= end_y; c++)
                   map[r][c] = 1;
      }

       // BFS
       sizeList = new ArrayList<>();
       queue = new LinkedList<>();
       for(int i=1; i<=N; i++)
           for(int j=1; j<=M; j++)
               if(map[i][j] == 0 && !visited[i][j]){
                   ++result;
                   visited[i][j] = true;
                   queue.offer(new Pos(i, j));
                   BFS();
              }

       // 각 영역별 크기 정렬
       Collections.sort(sizeList);
       System.out.println(result);
       for(int i=0; i<sizeList.size(); i++)
           System.out.print(sizeList.get(i) + " ");

  }

}


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 10610 : 30  (0) 2019.02.01
[JAVA] 백준 2980 : 도로와 신호등  (0) 2019.01.21
[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준15686 : 치킨 배달  (0) 2018.11.23

문제

NxM의 체스판이 주어졌을 때(8 <= N, M <= 20), 보드를 잘라 8x8의 체스판을 만들려고 한다. 이 때 체스판의 흰x백 배열을 만들기 위해 최소로 다시 색칠해야하는 칸의 개수를 구하는 문제.



접근방법

  1. 체스판을 8x8씩 돌아가면서 다시 칠해야 하는 개수를 체크한다.

  2. 체스판의 첫번째 칸 색에 따라 동일한 규칙을 적용받는 방법을 이용한다.

    즉, 첫번째 칸이 'W'일 경우에는 (배열이 0부터 시작한다고 하면, )짝수열의 짝수행은 W, 홀수행은 B가 되어야 하며, 홀수열의 짝수행은 B, 홀수행은 W가 되어야 한다.

  3. 첫번째칸이 W인 경우와 B인 경우를 고려해 더 적은 수를 선택하여, 전체 중 가장 작은 수를 구한다. (처음에는 첫번째 칸의 색을 기준으로 비교를 했지만, 첫번째 칸을 다른 색으로 칠했을 때 더 조금만 칠해서 체스판을 만들 수 있는 경우가 있으므로, 첫번째 칸의 색에 관계 없이 모두 확인해 주어야 한다.)




소스코드

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

   String input[] = in.readLine().split(" ");
   int N = Integer.parseInt(input[0]);
   int M = Integer.parseInt(input[1]);

   char[][] map = new char[N][M];
   for(int i=0; i<N; i++){
       String line = in.readLine();
       for(int j=0; j<M; j++)
           map[i][j] = line.charAt(j);
  }

   int min = 64;
   for(int i=0; i<N-8+1; i++){
       for(int j=0; j<M-8+1; j++){

           // B로 시작하는 체스판과 비교
           int diff_B = 0;
           for(int a=0; a<8; a++) {
               for (int b = 0; b < 8; b++) {
                   // B로 시작하면 짝수열(0,2..) - 짝수행 'B', 홀수행 'W'
                   //           홀수열(1,3..) - 짝수행 'W', 홀수행 'B'
                   if (a % 2 == 0) {
                       if (b % 2 == 0) {
                           if (map[i + a][j + b] != 'B')
                               ++diff_B;
                      } else {
                           if (map[i + a][j + b] != 'W')
                               ++diff_B;
                      }
                  } else {
                       if (b % 2 == 0) {
                           if (map[i + a][j + b] != 'W')
                               ++diff_B;
                      } else {
                           if (map[i + a][j + b] != 'B')
                               ++diff_B;
                      }
                  }
              }
          }

           // W로 시작하는 체스판과 비교
           int diff_W = 0;
           for(int a=0; a<8; a++)
                   for(int b=0; b<8; b++){
                       // W로 시작하면 짝수열(0,2..) - 짝수행 'W', 홀수행 'B'
                       //           홀수열(1,3..) - 짝수행 'B', 홀수행 'W'
                       if(a%2 == 0){
                           if(b%2 == 0){
                               if(map[i+a][j+b] != 'W')
                                   ++diff_W;
                          }else{
                               if(map[i+a][j+b] != 'B')
                                   ++diff_W;
                          }
                      }else{
                           if(b%2 == 0){
                               if(map[i+a][j+b] != 'B')
                                   ++diff_W;
                          }else{
                               if(map[i+a][j+b] != 'W')
                                   ++diff_W;
                          }
                      }
                  }

           // 최솟값 비교
           int dif_min = (diff_W < diff_B) ? diff_W : diff_B;
           if(dif_min < min)
               min = dif_min;
      }
  }
   System.out.println(min);
}


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 2980 : 도로와 신호등  (0) 2019.01.21
[JAVA] 백준 2593 : 영역 구하기  (0) 2019.01.20
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준15686 : 치킨 배달  (0) 2018.11.23
백준16234 : 인구 이동  (0) 2018.11.21

[JAVA] 백준 1120 : 문자열

문제 : 두 문자열 A, B가 주어질 때(A의길이 <= B의 길이), A앞에 아무 알파벳이나 추가하고, A뒤에 아무 알파벳이나 추가하여 B와 길이가 같게 만들어 A[i] B[i]의 차이가 최소인 문자열을 만들고, 그 차이를 출력하는 문제


접근방법

  1. 주어진 A를 B와 일치하는 알파벳이 가장 많은 곳에 위치시키고, 나머지를 B와 동일한 알파벳으로 채우면 차이가 최소가 된다.

    예를들어, abc, cbabcaa가 주어질 때

    __abc__ cbabcaa 와 같이 abc를 위치시키면,

    A의 앞뒤를 B와 동일하게 cbabcaa로 만들면 차이가 최소가 된다.

  2. A의 앞뒤를 B와 동일하게 만들면 새로 추가한 부분에 대한 차이값은 0이므로, 별도로 고려해주지 않아도 된다.

    즉, A와 B가 가장 많이 일치하는 위치를 찾고, 그 때의 차이값을 출력해주면 된다.

  3. B에서 A의 길이만큼의 부분문자열을 얻어와 A와 비교하는 방법을 사용한다.

    String temp_B = B.substring(i, i+A.length());
    int same = 0;
    for(int j=0; j<temp_B.length(); j++){
       if(A.charAt(j) == temp_B.charAt(j))
           ++same;
    }

소스코드

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String input[] = in.readLine().split(" ");
String A = input[0];
String B = input[1];

int max_same = 0;
for(int i=0; i<B.length()-A.length()+1; i++){
   String temp_B = B.substring(i, i+A.length());
   int same = 0;
   for(int j=0; j<temp_B.length(); j++){
       if(A.charAt(j) == temp_B.charAt(j))
           ++same;
  }
   if(same > max_same)
       max_same = same;
}

System.out.println(A.length() - max_same);


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 2593 : 영역 구하기  (0) 2019.01.20
[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
백준15686 : 치킨 배달  (0) 2018.11.23
백준16234 : 인구 이동  (0) 2018.11.21
백준15684 : 사다리 조작  (0) 2018.11.21

프래그먼트 뒤로가기

안드로이드 화면을 Activity가 아닌 Fragment로 구성할 때에는 별도로 뒤로가기 처리를 해줘야한다.

즉, 프래그먼트 A -> B-> C 로 이동했을 때에, Back Key를 눌렀을 때 C->B->A 로 돌아갈 수 있도록 해줘야한다. 그렇지 않으면 바로 앱이 종료되기 때문이다.

구글링해서 나온 여러 코드들은 리스너 interface를 구현해서 사용하는데, 이해가 잘되지 않아 Stack을 이용해 쉽게 구현하는 방법을 사용했다.

예제는 세 개의 프래그먼트를 만들어놓고, 버튼을 누르면 First -> Second -> Third 프래그먼트로 이동할 수 있도록 했고, Back Key를 누르면 직전 프래그먼트로 이동할 수 있도록 하였다.

  1. 먼저 Fragment 스택을 만들어 놓고, 기본적으로 들어갈 프래그먼트를 push해준다.

    public static Stack<Fragment> fragmentStack;

    firstFragment = new FirstFragment();
    secondFragment = new SecondFragment();
    thirdFragment = new ThirdFragment();

    fragmentStack = new Stack<>();
    fragmentStack.push(firstFragment);
    manager = getSupportFragmentManager();
    manager.beginTransaction().replace(R.id.container, firstFragment).commit();
    • 위의 코드는 MainActivity 의 onCreate() 메소드에 넣어준다.

  2. 다음 프래그먼트로 이동하는 버튼을 누를 때, 프래그먼트 replace 전에 현 프래그먼트를 스택에 저장해준다.

    • 프래그먼트 이동은 MainActivity에 changeFragment() 라는 메소드를 별도로 정의해 이용하였다.

    • 현재 프래그먼트를 가져오는 코드는 아래와 같다.

      Fragment currentFragment = MainActivity.manager.findFragmentById(R.id.container);

      • id container는 프래그먼트를 넣어주는 프레임레이아웃 id이다.

    • button.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             Fragment currentFragment = MainActivity.manager.findFragmentById(R.id.container);

             // 이동버튼 클릭할 때 stack에 push
             MainActivity.fragmentStack.push(currentFragment);
             MainActivity.changeFragment(MainActivity.FRAGMENT_SECOND);
        }
      });
  3. 뒤로가기를 누를 때 스택이 비어있지 않다면 마지막 원소를 pop 하여 해당 프래그먼트로 이동한다.

  • 전체소스코드

    1) MainActivity.java

    public class MainActivity extends AppCompatActivity {

       final static int FRAGMENT_FIRST = 1001;
       final static int FRAGMENT_SECOND = 1002;
       final static int FRAGMENT_THIRD = 1003;

       public static FirstFragment firstFragment;
       public static SecondFragment secondFragment;
       public static ThirdFragment thirdFragment;
       public static FragmentManager manager;

       public static Stack<Fragment> fragmentStack;

       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);

           firstFragment = new FirstFragment();
           secondFragment = new SecondFragment();
           thirdFragment = new ThirdFragment();

           fragmentStack = new Stack<>();
           fragmentStack.push(firstFragment);
           manager = getSupportFragmentManager();
           manager.beginTransaction().replace(R.id.container, firstFragment).commit();

      }

       public static void changeFragment(int index){
           switch (index){
               case FRAGMENT_FIRST:
                   manager.beginTransaction().replace(R.id.container, firstFragment).commit();
                   break;
               case FRAGMENT_SECOND:
                   manager.beginTransaction().replace(R.id.container, secondFragment).commit();
                   break;
               case FRAGMENT_THIRD:
                   manager.beginTransaction().add(R.id.container, thirdFragment).commit();
                   break;
          }
      }

       @Override
       public void onBackPressed() {
           if(!fragmentStack.isEmpty()){
               Fragment nextFragment = fragmentStack.pop();
               manager.beginTransaction().replace(R.id.container, nextFragment).commit();
               System.out.println("[TESTING >>] " + fragmentStack.size());
          }else {
               super.onBackPressed();
          }

      }
    }

2) FirstFragment.java

public class FirstFragment extends Fragment {

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable Bundle savedInstanceState) {
       ViewGroup rootView = (ViewGroup)inflater.inflate(R.layout.fragment_first, container, false);
       button = rootView.findViewById(R.id.btn_first);
       button.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Fragment currentFragment = MainActivity.manager.findFragmentById(R.id.container);

               // 이동버튼 클릭할 때 stack에 push
               MainActivity.fragmentStack.push(currentFragment);
               MainActivity.changeFragment(MainActivity.FRAGMENT_SECOND);
          }
      });

       return rootView;
  }

}
  • Second / Third 프래그먼트도 위와 동일하다.


'Android' 카테고리의 다른 글

[Android Error] String resource ID #0x0  (0) 2018.12.07

위와같은 에러를 자주봤는데, 이는 String 값을 인자로 줘야하는데 int와 같은 자료형을 인자로 전달할 때에 발생하는 에러다.


예를 들어

Toast.makeText(getContext(), businessList.size(), Toast.LENGTH_LONG).show();

위와 같이 입력하면 Stirng이 들어가야할 자리에 businessList.size()라는 int 값이 들어가기 때문에 오류가 발생한다.


Toast.makeText(getContext(), businessList.size()+"", Toast.LENGTH_LONG).show();

이와같이 "" 를 붙여 자동으로 String 으로 형변환되도록 해야한다.

'Android' 카테고리의 다른 글

[Android] Stack을 이용한 프래그먼트 뒤로가기  (1) 2018.12.09


백준15686 : 치킨 배달


문제

NxN 배열 빈칸(0), 집(1), 치킨집(2) 이 있을 때, 이 중 치킨집을 M개만 남겼을 때, 최대가 되는 치킨거리를 구하는 문제. 치킨거리는 각 집에서 가장 가까운 치킨집의 거리를 모두 더한 값이다.

https://www.acmicpc.net/problem/15686


접근방법

  • 치킨 리스트 중 M개를 선택한다. (combination 이용)

  • 각 집 당, 고른 M개의 치킨집과의 거리를 비교해 가장 짧은 거리를 구하고, 이들을 모두 더한 치킨거리를 구한다.

  • 최소의 치킨거리를 구한다.

  1. 치킨집 M개 고르기

    • 치킨집을 ArrayList에 저장하고, combination 함수를 이용해 고를 치킨집의 인덱스를 구한다.

  2. 각 집의 치킨거리 구하기

    • 모든 집과 모든 치킨집을 비교해 최솟값을 구하고, 각 집의 최솟값을 더해 치킨거리를 구한다.


소스코드

import java.util.*;
import java.io.*;

public class Main{

   static final int MAX = Integer.MAX_VALUE;
   static int N;
   static int M;
   static int map[][];
   static int min_dist;
   static ArrayList<Pos> home_list;
   static ArrayList<Pos> chicken_list;

   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x = x; this.y = y;}
  }

   public static void combination(int arr[], int index, int n, int r, int target) {
       if(r == 0) {
           int cur_chick_dist = chickDist(arr);
           if(cur_chick_dist < min_dist)
               min_dist = cur_chick_dist;
      }else if(target == n) {
           return;
      }else {
           arr[index] = target;
           combination(arr, index+1, n, r-1, target+1);
           combination(arr, index, n, r, target+1);
      }
  }

   static int chickDist(int[] arr) {
       int sum = 0;   // M개 치킨집을 고른 맵에서의 치킨거리

       int[][] map_tmp = new int[N][N];
       for(int i=0; i<N; i++)
           for(int j=0; j<N; j++) {
               map_tmp[i][j] = map[i][j];
               if(map_tmp[i][j] == 2)
                   map_tmp[i][j] = 0; // 치킨집 초기화(아무 치킨집도 선택되지 않은 상태)
          }

       // 치킨집 선택
       ArrayList<Pos> tmp_chickList = new ArrayList();
       for(int i=0; i<arr.length; i++) {
           Pos chick_pos = chicken_list.get(arr[i]);
           map_tmp[chick_pos.x][chick_pos.y] = 2;
           tmp_chickList.add(chick_pos);
      }

       // 각 사람당 가장 가까운 치킨집과의 거리 계산 후 합산
       for(int i=0; i<home_list.size(); i++) {
           int person_min_dist = MAX;
           for(int j=0; j<M; j++) {
               int cur_dist = 0;
               cur_dist = Math.abs(home_list.get(i).x - tmp_chickList.get(j).x)
                       + Math.abs(home_list.get(i).y - tmp_chickList.get(j).y);
               if(cur_dist < person_min_dist)
                   person_min_dist = cur_dist;
          }
           sum += person_min_dist;
      }

       return sum;
  }

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

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

       String input[] = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       M = Integer.parseInt(input[1]);
       map = new int[N][N];
       home_list = new ArrayList();
       chicken_list = new ArrayList();

       for(int i=0; i<N; i++) {
           String line[] = in.readLine().split(" ");
           for(int j=0; j<N; j++) {
               map[i][j] = Integer.parseInt(line[j]);
               if(map[i][j] == 1)
                   home_list.add(new Pos(i, j));
               else if(map[i][j] == 2)
                   chicken_list.add(new Pos(i,j));
          }
      }

       min_dist = MAX;
       combination(new int[M], 0, chicken_list.size(), M, 0);

       System.out.println(min_dist);


  }

}


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준16234 : 인구 이동  (0) 2018.11.21
백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16

[JAVA] 조합(Combination)

부르트포스 유형의 알고리즘을 풀다보면 n개 중에 r개를 뽑아 일일이 함수를 실행해서 결과값을 찾는 문제가 자주 나온다. 이때 조합 함수를 정의하여 사용하며, 기본 형식은 아래와 같다.

void combination(int arr[], int index, int n, int c, int target);


위 함수는 조합의 아래의 기본 공식을 이용한다.

= +


예를 들어 인 경우, (1,2) (1,3) (1,4) / (2,3) (2,4) (3,4) 의 경우가 존재하는데

1을 선택하는 경우와 1을 선택하지 않은 경우로 나눌 수 있다.

  • 1을 선택하는 앞의 경우에는 1을 이미 선택했으므로, 나머지 3개 중 1개를 고르는 이 남은 것이고,

  • 1을 선택하지 않은 경우는 1을 선택하지 않았았으므로, 나머지 중에 2개를 고르는 가 남는 것이다.

즉, = + 가 된다.


위 공식을 이용해 조합의 개수를 구하는 함수는 아래와 같다.

int combi(int n, int r){
 if(r==0 || r==n)
     return 1;
  else
      return combi(n-1, r-1) + combi(n-1, r);
}


조합의 개수를 구하는 것이라 경우의 수 각각을 구하는 함수는 아래와 같다.

java


static void combi(int[] arr, int index, int n, int r, int target) {
if(r==0) {
for(int i=0; i<arr.length; i++)
System.out.print(arr[i] + " ");
System.out.println();
}else if(target == n) {
return;
}else {
arr[index] = target;
combi(arr, index+1, n, r-1, target+1);
combi(arr, index, n, r, target+1);
}
}


arr[] 은 조합으로 고른 원소가 들어가 있는 배열이다. 배열의 크기는 r과 동일해야 한다.

  • 예를 들어 의 경우는 arr[] 이 {1, 2}, {1, 3} , ... , {3, 4} 의 원소를 갖고 있을 때 if(r==0) 부분이 실행된다. 해당 조합을 가지고 어떤 기능을 수행할 때에는 arr을 인자로 보내는 함수를 여기서 수행하면 된다.

  • int n, int r 의 n, r이 들어가면 된다.

  • index는 구한 원소의 개수를 의미한다. 즉, 위처럼 해당 원소를 선택한 경우는 하나의 원소가 선택되었으므로 idnex+1로 인자를 보내주고, r-1개를 뽑으면 되므로 , r-1을 인자로 보내준다. 해당원소를 선택하지 않은 경우는 Index가 그대로 유지되며, r도 그대로 유지된다. (이때는 target만 1 증가한다.)

  • target은 몇부터 원소를 선택할지를 의미한다. 예를 들어 의 경우를 예로 들 때 target = 1은 (1,2) (1,3) (1,4) 를 말하며, target=2일 경우에는 (2,3) (2,4) .. 를 말한다. index의 원소를 선택하거나 안하거나를 결정했으므로, target을 1 증가시켜준다.


combination 함수에서는 (0,1,2) (2,3,4).. 등과 같이 자연수 n의 조합을 출력하므로, 2차원 배열의 좌표와 같은 경우에는 (x,y)를 갖는 객체 리스트를 만들어 놓은 다음 combination 함수를 이용해 자연수 조합을 배열 arr로 받은 다음 이 배열을 리스트에 접근하는 원소로 사용할 수 있다.

소스코드 :

ArrayList<Pos> virus_list = new ArrayList();
for(int i=1; i<=N; i++)
for(int j=1; j<=M; j++)
if(map[i][j] == 2) {// 바이러스일 경우 바이러스리스트에 넣기
virus_list.add(new Pos(i, j));
           
/*....*/
//virus_list 중 3개를 선택하는 경우
combination(new int[3], 0, virus_list.size(), 3, 0);
   

static void combination(int arr[], int index, int n, int r, int target) {
if(r == 0) {
           // 좌표 조합 (x,y) 출력
           for(int i=0; i<arr.length; i++)
      System.out.print("(" + virus_list.get(arr[0]).x + ","
                                +  virus_list.get(arr[0]).y + ")");
           System.out.println();
}else if(target == n) {
return;
}else {
arr[index] = target;
combination(arr, index+1, n, r-1, target+1 );
combination(arr, index, n, r, target+1);
}
}

'JAVA' 카테고리의 다른 글

[JAVA] ArrayList 배열로 바꾸기  (0) 2018.11.21
[JAVA] 입력 클래스  (0) 2018.10.10
[JAVA] 이진탐색트리  (1) 2018.10.09
[JAVA] Scanner함수 next(), nextLine() 차이  (0) 2018.09.20

백준16234 : 인구 이동

문제

2차원 배열에 국가별 인구 수가 주어져있을 때 인접한 국가의 인구수가 주어진 범위 내에 있으면 국경을 열어 인구이동을 하여 1/N인구로 만드는 작업을 반복할 때, 인구이동이 몇 번 일어나는 지 알아내는 문제

https://www.acmicpc.net/problem/16234



접근방법

  • BFS를 이용해 인접한 영역을 확인하고 인구이동을 한다.

  • 인구이동을 한 국가는 booelan moved[][] 를 이용해 표시한다.

  • 한 번 전체 국가가 인구이동을 한 뒤에는 이동 수를 증가시키고 moved[][]를 초기화 해준다.


  1. BFS를 이용해 인접한 영역 확인하기

    • 2개의 boolean 2차원 배열을 사용해주었다.

      1) BFS 체크용 boolean 배열 check[][] 시간이 초과되지 않도록 이미 방문한 배열 체크

      2) 인구이동한 국가 확인용 boolean 배열 moved[][]

    • 인접한 배열을 큐에 넣을지 결정하기 위해서는 다음의 네가지를 확인해주어야 한다.

      1) 배열의 범위를 벗어났는가

      • 이 경우는 애초에 배열을 2칸씩 크게 만들어서, 해당 원소의 값이 0인지 여부만 확인해주면 된다.

      2) 아직 인구이동이 되지 않은 지역인가

      • moved 배열 확인

      3) BFS가 이미 방문한 원소인가

      • check 배열 확인

      4) 인구차이가 범위 내에 있는가

    • 위 네가지를 확인한 뒤에 큐에 넣어준다.


    인구이동이 일어난 국가들의 인구 수를 변경해주기 위해, 큐에서 빼낸 좌표들은 다시 새로운 큐(queue_tmp)에 넣어준다.

    • 해당 큐를 이용하여 인구 수를 설정해주고, moved를 함께 체크한다.

    • 인접값을 확인하기 전부터 미리 하나는 큐에 들어가므로, 큐의 개수가 1개 초과일 경우가 인구이동이 일어난 경우이다.




소스코드

import java.util.*;
import java.io.*;

public class Main{

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

   static int N;
   static int minGap;
   static int maxGap;
   static int[][] map;
   static boolean[][] moved;
   static boolean isQuit;

   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x= x; this.y = y;}
  }

   static void BFS(int x, int y){
       boolean check[][] = new boolean[N+2][N+2];
       Queue<Pos> queue = new LinkedList();
       Queue<Pos> queue_tmp = new LinkedList();
       queue.offer(new Pos(x, y));

       int sum = 0;
       while(!queue.isEmpty()) {
           Pos cur_pos = queue.poll();
           check[cur_pos.x][cur_pos.y] = true;
           sum += map[cur_pos.x][cur_pos.y];
           queue_tmp.offer(new Pos(cur_pos.x, cur_pos.y));

           for (int i = 0; i < 4; i++) {
               Pos next_pos = new Pos(cur_pos.x + dx[i], cur_pos.y + dy[i]);
               int gap = Math.abs(map[cur_pos.x][cur_pos.y] - map[next_pos.x][next_pos.y]);
               if( map[next_pos.x][next_pos.y] != 0        // 경계가 아니면서
                       && !moved[next_pos.x][next_pos.y]   // 아직 인구이동이 되지 않은 지역이면서
                       && !check[next_pos.x][next_pos.y]   // <<BFS방문체크>>
                       && gap >= minGap && gap <= maxGap){ // 인구 차이가 범위 내인 경우
                   queue.offer(next_pos);                  // 큐에 넣어준다.
                   check[next_pos.x][next_pos.y] = true;
              }
          }
      }

       // 인구이동이 일어난 경우
       if(queue_tmp.size() != 1){
           isQuit = false;
           int value = sum / queue_tmp.size();
           while(!queue_tmp.isEmpty()){
               Pos pos = queue_tmp.poll();
               map[pos.x][pos.y] = value;
               moved[pos.x][pos.y] = true;
          }
      }

  }


   public static void main(String[] args) throws IOException{
       BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
       String[] input = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       minGap = Integer.parseInt(input[1]);
       maxGap = Integer.parseInt(input[2]);

       map = new int[N+2][N+2];
       moved = new boolean[N+2][N+2];
       for(int i=1; i<=N; i++){
           String line[] = in.readLine().split(" ");
           for(int j=1; j<=N; j++)
               map[i][j] = Integer.parseInt(line[j-1]);
      }

       int result = 0;
       while(true){
           isQuit = true;
           for(int i=1; i<=N ;i++)
               for (int j = 1; j <= N; j++)
                   if (!moved[i][j])
                       BFS(i, j);

           if(!isQuit){
               ++result;
               // 인구이동여부 초기화
               for(int i=1; i<=N; i++)
                   for(int j=1; j<=N; j++)
                       moved[i][j] = false;
          } else{ // 인구이동 하나도 없을 시 종료
               break;
          }
      }

       System.out.println(result);
  }
}


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준15686 : 치킨 배달  (0) 2018.11.23
백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27

백준15684 : 사다리 조작

문제

N개의 세로선과 H개의 가로선이 있을 때 i번째 세로선을 타고 다시 i번째로 끝나기 위해 추가해야 될 가로선의 개수를 구하는 문제

https://www.acmicpc.net/problem/15684


접근방법

  • 3번까지만 사다리를 놓는다는 것을 보고 1번, 2번, 3번 사다리를 일일이 놓아본 다음 다시 본인의 세로선에서 끝나는지를 확인하도록 하였다.

  • 기본로직

    // i-i 연결 여부 확인
    if(search(map) == false){ // 초기상태 확인
       ++result;
       isTrue = combi_1(); // 가로선 1개 추가 후 확인
       if(isTrue == false){
           ++result;
      isTrue = combi_2(); // 가로선 2개 추가 후 확인
           if(isTrue == false){
               ++result;
               isTrue = combi_3(); // 가로선 3개 추가 후 확인
               if(isTrue == false)
                   result = -1;
          }
      }
    }
    System.out.println(result);


  1. i번째 세로선이 사다리를 타고 도달하는 세로선 확인하는 함수 search()

    • 먼저 입력을 받을 때 가로는 N-1개, 세로는 H개의 배열로 받아지는 것을 알 수 있다. 즉, map[i][j] 는 i번째 가로선과 j와 j+1번째 세로선을 잇는 선이 된다.

    • map[H][N-1] 배열에서 i번째 세로선에서 시작해 사다리를 타고 이동할 곳을 알기 위해서는 map[i][j-1]map[i][j] 을 확인해야 한다. 즉, 첫번째경우가 true라면 왼쪽으로 이동하는 것이고, 두번재 경우가 true라면 오른쪽으로 이동하는 경우이다.

    • 여기서 첫번째 세로선과 마지막 세로선은 한방향으로만 이동할 수 있기 때문에 예외처리를 해주어야 한다.

    • map[i][j-1], map[i][j] 에 따라서 세로선 인덱스가 바뀌기 때문에, 마지막에 처음 시작한 인덱스와 비교해주기 위해 n_tmp 를 사용하였다.

    • 소스코드는 아래와 같다.

      // i-i 연결여부 확인
      static boolean search(boolean[][] map){

         boolean result = true;
         for(int n=1; n<=N; n++){
             int n_tmp = n;
             for(int h=1; h<=H; h++) {
                 // 왼쪽으로 이동 (1은 확인x)
                 if (n > 1 && map[h][n - 1] == true) {
                     --n;
                }// 오른쪽으로 이동 (N은 확인x)
                 else if (n < N && map[h][n] == true){
                     ++n;
                }
            }
             if(n != n_tmp){
                 result = false;
                 break;
            }
             n = n_tmp;
        }

         return result;
      }


  1. 가로선을 하나 추가하는 경우는 이차원 배열을 훑으면서 하나씩 가로선을 더해주면 된다.

    • 소스코드

      static void combi_1(){
        boolean map_tmp[][] = new boolean[H+1][N];
         for(int i=1; i<=H; i++)
             for(int j=1; j<=N-1; j++)
                 map_tmp[i][j] = map[i][j];

         for(int i=0; i<pos_set.length; i++){
             if(pos_set[i] != null){
                 map_tmp[pos_set[i].x][pos_set[i].y] = true;
                 if(search(map_tmp)){
                     isTrue = true;
                     break;
                }// 바뀐 맵 원래대로 돌려놓기
                 map_tmp[pos_set[i].x][pos_set[i].y] = false;
            }
        }
      }


  1. 가로선을 2개, 3개 추가하는 경우는 combination함수를 사용하였다.

    • (x, y)의 위치를 조합으로 꺼내야하기 때문에, Pos 객체를 이용해 가로선이 없는 위치를 배열에 넣어주었다.


소스코드

import java.io.*;
import java.lang.reflect.Array;
import java.util.ArrayList;

public class Main{

   static int N;
   static int M;
   static int H;
   static boolean isTrue;
   static boolean map[][];
   static Pos[] pos_set = new Pos[H*(N-1)];


   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x = x; this.y = y;}
  }

   // i-i 연결여부 확인
   static boolean search(boolean[][] map){

       boolean result = true;
       for(int n=1; n<=N; n++){
           int n_tmp = n;
           for(int h=1; h<=H; h++) {
               // 왼쪽으로 이동 (1은 확인x)
               if (n > 1 && map[h][n - 1] == true) {
                   --n;
              }// 오른쪽으로 이동 (N은 확인x)
               else if (n < N && map[h][n] == true){
                   ++n;
              }
          }
           if(n != n_tmp){
               result = false;
               break;
          }
           n = n_tmp;
      }

       return result;
  }

   static void combi_1(){
      boolean map_tmp[][] = new boolean[H+1][N];
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
               map_tmp[i][j] = map[i][j];

       for(int i=0; i<pos_set.length; i++){
           if(pos_set[i] != null){
               map_tmp[pos_set[i].x][pos_set[i].y] = true;
               if(search(map_tmp)){
                   isTrue = true;
                   break;
              }// 바뀐 맵 원래대로 돌려놓기
               map_tmp[pos_set[i].x][pos_set[i].y] = false;
          }
      }
  }

   static void combi_2(int arr[]){
       boolean map_tmp[][] = new boolean[H+1][N];
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
               map_tmp[i][j] = map[i][j];

       map_tmp[pos_set[arr[0]].x][pos_set[arr[0]].y] = true;
       map_tmp[pos_set[arr[1]].x][pos_set[arr[1]].y] = true;

       if(search(map_tmp))
           isTrue = true;
  }

   static void combi_3(int arr[]){
       boolean map_tmp[][] = new boolean[H+1][N];
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
               map_tmp[i][j] = map[i][j];

       map_tmp[pos_set[arr[0]].x][pos_set[arr[0]].y] = true;
       map_tmp[pos_set[arr[1]].x][pos_set[arr[1]].y] = true;
       map_tmp[pos_set[arr[2]].x][pos_set[arr[2]].y] = true;

       if(search(map_tmp))
           isTrue = true;
  }

   static void combination(int arr[], int index, int n, int r, int target){
       if(arr.length == 1) {
           combi_1();
      }else {
           if (r == 0) {
               if (arr.length == 2) { // 두 개 고르는 경우
                   combi_2(arr);
              } else if (arr.length == 3) { // 세 개 고르는 경우
                   combi_3(arr);
              }
          } else if (target == n) {
               return;
          } else {
               arr[index] = target;
               combination(arr, index + 1, n, r - 1, target + 1);
               combination(arr, index, n, r, target + 1);
          }
      }
  }


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

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

       String input[] = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       M = Integer.parseInt(input[1]);
       H = Integer.parseInt(input[2]);
       map= new boolean[H+1][N]; // 한 칸씩 크게 맵을 만들어 줌(H x N-1)

       for(int i=0; i<M; i++){
           String line[] = in.readLine().split(" ");
           map[Integer.parseInt(line[0])][Integer.parseInt(line[1])] = true;
      }

       isTrue = false;
       int result = 0;

       //pos_set = new Pos[H*(N-1)];
       ArrayList<Pos> pos_list = new ArrayList();
       int p_index = 0;
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
              if(map[i][j] == false)
                  pos_list.add(new Pos(i, j));
       pos_set = pos_list.toArray(new Pos[p_index]);

       // i-i 연결 여부 확인
       if(search(map) == false){ // 초기상태 확인
           ++result;
           int arr_1[] = new int[1];
           combination(arr_1,0, pos_set.length, 1, 0);
           if(isTrue == false){ // 가로선 1개 추가 후 확인
               ++result;
               int arr_2[] = new int[2]; // 가로선 2개 추가 후 확인
               combination(arr_2,0, pos_set.length, 2, 0);
               if(isTrue == false){
                   ++result;
                   int arr_3[] = new int[3];
                   combination(arr_3,0, pos_set.length, 3, 0);
                   if(isTrue == false)
                       result = -1;
              }
          }
      }

       System.out.println(result);

  }
}


- 정답은 맞았지만 시간 상 수정이 필요하다.

'알고리즘' 카테고리의 다른 글

백준15686 : 치킨 배달  (0) 2018.11.23
백준16234 : 인구 이동  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20

+ Recent posts