Algorithm

[BOJ] 15684번 : 사다리 조작

rudgh99_algo 2024. 9. 11. 19:15

1. problem : 

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

 

2. solution 1 :

#include <bits/stdc++.h>
using namespace std;
int n, m, h;
bool ladders[35][15]; 
vector<pair<int, int>> v; // 사다리를 설치할 수 있는 좌표들의 모음집
int ans = INT_MAX;
bool check() {
	for (int i = 1; i <= n; i++) { // 열
		int cur = i; 
		for (int j = 1; j <= h; j++) { // 행
			if (ladders[j][cur]) cur++;
			else if (ladders[j][cur - 1]) cur--;
		}
		if (cur != i) return false;
	}
	return true;
}

void backTrack(int idx, int k, int cnt) {
	if (k == cnt) {
		if (check()) ans = min(ans, cnt);
		return;
	}
	for (int i = idx; i < v.size(); i++) {
		if (!ladders[v[i].first][v[i].second]) {
			ladders[v[i].first][v[i].second] = true; 
			backTrack(i + 1,k + 1, cnt); 
			ladders[v[i].first][v[i].second] = false; 
		}
	}
}

int main(void) {
	ios::sync_with_stdio(0);
	cin.tie(0); 
	cin >> n >> m >> h; 
	for (int i = 0; i < m; i++) {
		int a, b;
		cin >> a >> b; 
		ladders[a][b] = true; 
	}
	// 사다리를 설치하는 가능한 경우의 수를 다 저장 
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j < n; j++) {
			if (ladders[i][j] || ladders[i][j + 1] || ladders[i][j - 1]) continue; 
			v.push_back({ i,j });
		}
	}
	for (int i = 0; i <= 3; i++) {
		backTrack(0, 0, i);
		if (ans != INT_MAX) break;
	}
	if (ans == INT_MAX) cout << -1 << '\n';
	else cout << ans;

}

backTracking을 이용하여, 풀었다. backTracking을 이용할 때는 parameter에 idx를 두어, 중복을 최대한 피하려고 노력하였다. (순열이 필요 없기 때문에) 내가 코드를 짜면서 , 생각하지 못했던 것은 , 모든 경우의 수를 vector에 집어넣고, 꺼내 쓰면 된다는 생각이다. 나는 이 생각을 하지 못해, do while next_permutation으로 풀어볼까?라고 생각했지만, vector의 크기와 1을 어떻게 추가해야 할지 고민하며, 코드가 꼬였다. 

 

3. solution 2 :

// Authored by : SciEm
// Co-authored by : BaaaaaaaaaaarkingDog
// http://boj.kr/74da3b10100849d68340661e3e748159
#include <bits/stdc++.h>
using namespace std;

#define X first
#define Y second

bool ladder[32][12];
int idxs[3];
vector<pair<int, int>> coords;  // 고를 수 있는 가로선만을 저장할 벡터
int n, m, h;

// i번째가 i인지 사다리를 타며 확인
bool check() {
  for (int j = 1; j <= n; j++) {
    int cur = j;
    for (int i = 1; i <= h; i++) {
      if (ladder[i][cur - 1]) cur--;
      else if (ladder[i][cur]) cur++;
    }
    if (cur != j) return false;
  }
  return true;
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> n >> m >> h;
  while (m--) {
    int a, b;
    cin >> a >> b;
    ladder[a][b] = true;
  }

  for (int i = 1; i <= h; i++)
    for (int j = 1; j < n; j++) {
      // 인접한 것은 애초에 넣지 않음
      if (ladder[i][j - 1] || ladder[i][j] || ladder[i][j + 1]) continue;
      coords.push_back({i, j});
    }


  if(check()){
    cout << 0;
    return 0;
  }

  int ans = 0x7f7f7f7f;
  int sz = coords.size();
  for(int i = 0; i < sz; i++){
    ladder[coords[i].X][coords[i].Y] = true;
    if(check()) ans = min(ans, 1);
    for(int j = i+1; j < sz; j++){
      ladder[coords[j].X][coords[j].Y] = true;
      if(check()) ans = min(ans, 2);
      for(int k = j+1; k < sz; k++){
        ladder[coords[k].X][coords[k].Y] = true;
        if(check()) ans = min(ans, 3);
        ladder[coords[k].X][coords[k].Y] = false;
      }
      ladder[coords[j].X][coords[j].Y] = false;
    }
    ladder[coords[i].X][coords[i].Y] = false;
  }
  if(ans == 0x7f7f7f7f) ans = -1;
  cout << ans;
}
/*
3중 for문 대신 백트래킹을 써도 됨
*/

source code 출처 : https://github.com/encrypted-def/basic-algo-lecture/blob/master/0x0D/solutions/15684.cpp

 

basic-algo-lecture/0x0D/solutions/15684.cpp at master · encrypted-def/basic-algo-lecture

바킹독의 실전 알고리즘 강의 자료. Contribute to encrypted-def/basic-algo-lecture development by creating an account on GitHub.

github.com

나도 이 풀이를 참고하여, 백트래킹으로 갔다. 하지만 ,  이 풀이에서는 삼중 for문을 이용해, 3개의 경우의 수를 커버하고 있다.