Gidhub BE Developer

[BOJ] 15684. 사다리 조작

2018-10-03
goodGid

Problem

Problem URL : 사다리 조작


[1] Answer Code (18. 10. 18)

#include<iostream>
#include<vector>
#include<cstring>
#define p pair<int,int>
using namespace std;

int ans;
int row,col,h;

int map[275][15];
int target_line;

bool chk(){
    for(int i=1; i<=col; i++){
        int origin_c_idx = i;
        int r_idx = 1;
        int c_idx = i;
        
        while (1) {
            if(map[r_idx][c_idx] == 1) // 오른쪽으로 라인이 있는 경우
                c_idx ++;
            else if(map[r_idx][c_idx - 1] == 1) // 왼쪽으로 라인이 있는 경우
                c_idx --;
            r_idx ++; // 1 time 지나면 무조건 한 칸 아래로 이동
            
            if(r_idx == row + 1)
                break;
        } // end of while
        if(origin_c_idx != c_idx)
            return false;
    } // end of for i
    return true;
}

void dfs(int r_idx, int c_idx, int use_line_cnt){
    if( use_line_cnt == target_line ){
        if(chk()){
            ans = target_line;
        }
        return ;
    }
    
    for(int r=r_idx; r<=row; r++){
        /*
        나는 1,1이라고 입력이 주어진다면
        무조건 오른쪽으로만 Line을 놓는다 생각했다.
        
        그렇기 때문에 c는 col까지가 아닌 col-1까지만 돌린다.
        col이 5개라면
        5번째 col은 오른쪽으로 놓는 경우는 없기 때문이다.
        */
        for(int c=c_idx; c<col; c++){ 
            if(map[r][c] != 1 && map[r][c-1] != 1 && map[r][c+1] != 1){
                map[r][c] = 1;
                dfs(r,c+1,use_line_cnt + 1);
                map[r][c] = 0;
            }
        } // end of for c
        c_idx = 1;
    } // end of for r
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    ans = -1;
  
    cin >> col >> h >> row;
    
    for(int i=0; i<h; i++){
        int a,b;
        cin >> a >> b;
        map[a][b] = 1;
    }
    
    for(int i=0; i<4; i++){
        target_line = i;
        dfs(1,1,0);
        if(ans != -1)
            break;
    }
    cout << ans << endl;
    return 0;
}

Reivew

  • 544ms

  • 다시 푸니까 큰 문제 없이 풀었다.

  • 이번에는 목표 라인 수를 정해놓고 0 -> 3까지 DFS를 돌려서 시간을 단축시켰다.


[2] Answer Code (18. 10. 03)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int l[31][11]; // l is line
int row,col;
int ans = 2e9;

bool chk(){
    for(int i=1; i<=col; i++){
        int cp = i; // cp is column position
        int rp = 1; // rp is row position
        
        while (rp <= row) {
            if(l[rp][cp-1] == 1)
                cp--;
            else if(l[rp][cp] == 1)
                cp++;
            rp++;
        }
        if(cp != i)
            return false;
    }
    return true;
}

void dfs(int r, int c, int cnt){ // r is row, c is column, cnt is used line cnt
    if(cnt >= ans)
        return ;
    if( chk() ){
        ans = ans < cnt ? ans : cnt;
        return ;
    }
    if(cnt == 3 ) // [1]
        return ;
    for(int i=r; i<=row; i++){
        for(int j=1; j<=col-1; j++){
            if(l[i][j-1] == 1 || l[i][j] == 1 || l[i][j+1] == 1) continue;
            l[i][j] = 1;
            dfs(i,j, cnt+1 );
            l[i][j] = 0;
        }
    }
}

int main(){
    int n;
    cin >> col >> n >> row;
    
    for(int i=0; i<n; i++){
        int a,b;
        cin >> a >> b;
        l[a][b] = 1;
    }
    
    dfs(1,1,0);
    if( ans == 2e9)
        ans = -1;
    cout << ans << endl;
    return 0;
}

Review

  • 굉장히 어려웠다.

  • 막상 풀고나니까 명쾌하고 이런 유형에 대해서는 자신이 생긴 것 같다.

  • DFS 함수안 코드에 따라 시간이 확연히 다름을 알 수 있다.

if(cnt > 3 || cnt >= ans)
    return ;

if( chk() ){
    ans = ans < cnt ? ans : cnt;
    return ;
}

for(int i=r; i<=row; i++){
    for(int j=1; j<=col-1; j++){
        if(l[i][j-1] == 1 || l[i][j] == 1 || l[i][j+1] == 1) continue;
        l[i][j] = 1;
        dfs(i,j, cnt+1 );
        l[i][j] = 0;
    }
}
  • 996ms

  • if(cnt > 3 or cnt >= ans)처럼 하게 되면 cnt가 4가 되는 모든 경우를 살피게 된다.

  • 즉 cnt가 3일 때 chk()를 하고 조건을 충족시키지 못할 경우 바로 return을 해줘서 시간을 단축 시킬 수 있다.

  • 자세한건 아래 예시를 참고하자.

if( cnt >= ans)
    return ;

if( chk() ){
    ans = ans < cnt ? ans : cnt;
    return ;
}

if( cnt == 3 )
    return ;

for(int i=r; i<=row; i++){
    for(int j=1; j<=col-1; j++){
        if(l[i][j-1] == 1 || l[i][j] == 1 || l[i][j+1] == 1) continue;
        l[i][j] = 1;
        dfs(i,j, cnt+1 );
        l[i][j] = 0;
    }
}
  • 372ms

  • chk() 후 cnt == 3 일 경우에 cnt가 4가 될 경우를 가지치기한다.

  • 즉 cnt가 3일 때 chk()를 하였고 조건을 충족시키지 못했기 때문에 for loop를 돌기전에 return 시킨다.

if( cnt >= ans)
    return ;

if( chk() ){
    ans = ans < cnt ? ans : cnt;
    return ;
}

if( cnt == 3 )
    return ;

for(int i=r; i<=row; i++){
    for(int j=1; j<=col-1; j++){
        if(l[i][j-1] == 1 || l[i][j] == 1 || l[i][j+1] == 1) continue;
        l[i][j] = 1;
        dfs(r,c, cnt+1 ); // [1]
        l[i][j] = 0;
    }
}
  • 1972ms

  • [1] : 재귀 호출 시 매개 변수로 i,j가 아닌 r,c를 보내는 실수를 하였다.


[3] Wrong Code (18. 09. 24)

#include <iostream>
#include <cstring>
#define p pair<int,int>
using namespace std;

int l[272][32]; // l is line
int n,m;
int result;

bool chk(){
    int flag = 1;
    int st_x;
    int st_y;
    
    for(int i=1; i<=m; i++){
        if( ! flag )
            break;
        int v[272][32] = {0,}; // v is visit
        st_x = 1;
        st_y = i;
        v[st_x][st_y] = 1;
        
        /*
         좌우 있는지 체크
         없다면 아래로
         */
        while (1) {
            if(st_x == n+1){
                if(st_y != i){
                    flag = 0;
                }
                break;
            }
        
            if( l[st_x][st_y] && !v[st_x][st_y+1]){ // 오른쪽으로 생성된 라인이 있는가?
                v[st_x][st_y+1] = 1;
                st_y = st_y + 1;
            }
            else if( l[st_x][st_y-1] && !v[st_x][st_y-1]){ // 왼쪽으로 생성된 라인이 있는가?
                v[st_x][st_y-1] = 1;
                st_y = st_y - 1;
            }
            else{ // 좌우로 생성된 라인이 없으니 아래로 진행
                st_x ++;
                v[st_x][st_y] = 1;
            }
        } // end of while loop
    }
    return flag;
}


void print(){
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            cout << l[i][j] << " ";
        }
        cout << endl;
    }
}
int dfs(p pos, int dept){ // dept is depth
    bool ans = chk();
    if( dept >= result ) // 시간을 줄이기 위해 넣었지만 시간 초과를 해결하지 못했다.
        return -1;
    
    if(ans){
        return dept;
    }
    /*
     depth가 3일 때
     위에서 체크를 했고 그 값이 안되었기 때문에 return
     */
    if( dept >= 3){
        return -1;
    }
    
    // i means column
    // j means row
    int j = pos.first;
    for(int i=pos.second ; i<m; i++){
        for( ; j<=n; j++){
            if(l[j][i]) continue; // 이미 Line 존재
            if( l[j][i+1] != 0 || l[j][i-1] != 0) continue; // 좌우로 이미 연결된 라인이 있다면
            
            l[j][i] = dept + 6;
            /*
             1,3에 라인을 놓았다면
             그 다음 재귀에서는
             1,3 이후에 대해서만 체크를 하기 위해
             1,3을 위치값으로 넘깁니다.
             */
            int _tmp = dfs( p(j,i) , dept + 1);
            l[j][i] = 0;
            
            if( _tmp != -1 )
                return _tmp;
            
        } // end of for j
        if( j == n+1 )
            j = 1;
    } // end fo for i
    return -1;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    int cnt;
    cin >> m >> cnt >> n;
    
    for(int i=0; i<cnt; i++){
        int a,b;
        cin >> a >> b;
        l[a][b] = 1;
    }
    
    result = 2e9;
    for(int col=1 ; col<m; col++){
        for(int row=1 ; row<=n; row++){
            int tmp = dfs(p(row,col),0);
            if(tmp != -1 && result > tmp)
                result = tmp;
        }
    }
    if( result == 2e9)
        result = -1;
    cout << result << endl;
    return 0;
}

Review

  • 18년 상반기 삼성 역량 테스트 2번 기출 문제.

  • 무려 2시간 30분 코딩을 했는데 틀렸다 !

  • TC는 다 맞는데 어디가 틀리는지 몰라서 질문을 했고 반례를 찾아서 수정을 했더니 시간 초과가 났다 !

  • 가지치기 없이 모든 조건을 탐색하기 때문에 시간 초과가 날 수 밖에 없다.


Comments

Content