SW교육/알고리즘

고집센 개구리

GrayrabbiT 2016. 8. 4. 00:57
반응형

고집센 개구리

아이가 아푸다. 나도 기분이 매우 다운된다. 게다가 잘 먹던 약도 안먹는다. 약을 안먹으니 열이 엄청나게 올라가고 있다.힘들다. 어째거나…오늘 아는 형님이 문제 하나를 보여줬는데 거기에 양교수님 성함이 들어있었다. 찾아 뵙지도 못하고 송구하기만 하다. 근데 문제가 은근히 재미있었다.
내 수준이 낮으니 재미있었다가 맞겠다.

일단 문제부터 보자.

문제

  • 두 개구리 가족이 징검다리를 건너다 돌 하나를 사이에 두고 서로 마주쳤다.
    좁은 징검다리여서 한 가족이 뒤로 돌아 나와서 비켜주어야 지나갈 수 있지만
    개구리들은 고집이 세어서 서로 비켜주려하지 않는다.
  • 어느 개구리도 뒤로 돌아가지 않고 가던 길을 계속 갈 수 있도록 하려면 어떻게 해야 하는가?

목표

  • 최소의 움직임으로 왼쪽에 서 있던 개구리들을 오른쪽으로, 오른쪽에 있던 개구리들을 오른쪽으로 옮긴다.

    왼개굴1 왼개굴2 왼개굴3 빈칸 오른개굴1 오른개굴2 오른개굴3

뭐 이런상태다

규칙

  • 모든 개구리들은 돌 위에 서 있어야 한다.
  • 왼쪽에 있는 개구리는 오른쪽으로만, 오른쪽에 있는 개구리들은 왼쪽으로만 이동
  • 한 개구리 뒤에 빈 돌이 있으면, 그 개구리를 넘어 갈 수 있다.
  • 한 번에 한 개구리만 돌 하나만큼 움직일 수 있다.

뭐 이런 문제이다. 약간 헷갈릴 수 있는데 왼쪽 개구리를 L 오른쪽 개구리를 R빈칸을 0이라 해보자. 그렇다면 다음과 같이 표현될 수 있다.


L L L 0 R R R 뭐 이런 상태인거지.


그럼 이런 이동이 가능하다는 거다


L L L R 0 R R (R 개구리가 왼쪽으로 이동)


L L L R R 0 R (R 개구리가 쩜뿌. 물론 이렇게 이동하면 다음 이동이 불가능)


뭐 이런거 이다. 이를 해결하기 위해서 머리를 굴리다보니.. 당연히 움직여야 할 개구리가 정해진다는 것이고. 손으로 풀다보면 규칙이 보인다.

그 규칙은 이렇게 생각했다.

목표

일단 L R L R L R 의 형태가 반드시 되어야 한다.
만약 양 끝단이 아닌 곳에서 RR 혹은 LL 의 형태가 나타나면 망하는 것이다.
1. LRLRLRLR의 형태로 배치하고
2. RRRR LLLL의 형태로 만들어 나간다.

간단하다.


그렇다면 1번을 목표로 생각해 보자


L 0 R 의 상태라면 누구든지 움직일 수 있다. 우리는 편의상 R을 처음 움직이자. 


그렇다면 다음과 같이 움직일 수 있다.


L R 0 (1회 움직임)


그런데 만약 각각의 개구리가 더 있다고 가정해 보자


L L 0 R R 의 상태에서 R을 처음 움직이면 다음과 같이 배열된다.


L L R 0 R (1회 움직임)


그리고 나서 살펴보면 더 이상 R을 움직일 수 없다는 결론을 얻을 수 있다. 그렇다면 턴을 L로 넘기자 L을 움직이는 방법은


L 0 R L R (1회 움직임)


0 L R L R (2회 움직임)


L은 2회 움직일 수 있었으며 우리의 목표인 1번을 도달했다. 만약 3쌍짜리였다면 어떻게 되었을지 생각해보면 의외로 간단히 생각할 수 있는데 지금의 상태에서 양 끝에 L과 R을 붙이면 된다.


L 0 L R L R R


이 상태에며 L을 더 이상 움직이면 망하다는 것을 알 수 있다. 머리가 더 비상하다면 R을 이제 움직일 것이라는 것을 알 수 있으며, R은 3번 움직일 것이라 예상할 수 있다.
움직여 볼까?


L R L 0 L R R (1회 움직임)


L R L R L 0 R (2회 움직임)


L R L R L R 0 (3회 움직임)


그렇다면 우리의 목표 1번을 달성하기 위해서는 1+2+3+4+5….n회가 필요함을 알 수 있다.


그리고 2번 목표를 생각해 보자


L R L R L R 0 에서 L들을 움직이면 된다. 몇 번? 3번이다(n번) 이를 움직이면 다음과 같아 진다.

0 R L R L R L


이 상태에서 나는 꼭 2번만 움직이고 싶었다. 그런데 R을 3번 움직여야 하더라.. 그럼 생각을 고쳐먹고 이렇게 바꾸지


R 0 L R L R L 이렇게 하고 나면, R을 2번만 움직이면 된다(n-1)회


R R L 0 L R L (1회)

R R L R L 0 L (2회) 이상태에서 나는 1번만 움직이고 싶어졌다. 그래서 이렇게 바꾸고 생각한다.


R R L R 0 L L 그러면 L을 한번만 움직이면 된다.

R R 0 R L L L 우하하하… 그리고 안움직이고 싶었다. 그래서 R을 그냥 한번 움직인다.


R R R 0 L L L 이렇게 된다.


그렇다면 다시 목표 2를 위해서는 1 + 2+ 3+ 4+5+…n회에 꼼수 n번이 있었다.


결론 최후에는


시그마(웹에서는 시그마 어케 하는겨?) k = n*(n+1)/2 인데 이게 2개가 있으므로


n*n + n 에 꼼수 n을 더하면


결론 : n(n+2) 이 된다.

이하 소스코드이다.

#include <iostream>
#include <cstdio>

using namespace std;
int ar[10000];
void printfall(int n){
    cout<<"\nprint all data\n";
    for(int i=1;i<n*2+2;i++){
        cout<<ar[i]<<" ";
    }
    cout<<"\n";
}
//   3    3   2  1   1
//  m-2  m-1  m m+1 m+2
void generater(int n){
    int i;
    for(i=1;i<=n;i++){
        ar[i]=3;
    }
    ar[i]=2;
    for(++i;i<=2*n+1;i++){
        ar[i]=1;
    }
    ar[i]=3;
    ar[0]=1;
}




int main()
{
    int n=3; //몇쌍짜리 인가?

    cin>>n;
    generater(n);//일단 만들어라.

    int m=n+1; //가운데는 어디잉가?
    int di=1;//어디 방향부터 갈 것이냐?
    int cnt=0;
    int k=0;

    //이 녀석은 일단 31313131 혹은 1313131 의 형식으로 몰아버리는 녀석이다.
    for(int i=1;i<=n;i++){
        //i번 움직여야 한다.
        k = i;
        while(k--){
            if(ar[m-1]!=ar[m+1]){
                swap(ar[m],ar[m+di]);
                m=m+di;
                cnt++;
            }
            else
            {
                swap(ar[m],ar[m+(di*2)]);
                m=m+(di*2);
                cnt++;
            }
        }
        di*=-1;
    }
    //이녀석은 각 방향으로 돌아가면서 갈 수 있는 녀석들을 정리하는 녀석이다.
    for(int i=n;i>0;i--){
        k=i;
        while(k--){
            swap(ar[m+di*2],ar[m]);
            m=m+di*2;
            cnt++;
        }
        di*=-1;
        swap(ar[m+di],ar[m]);
        m=m+di;
        cnt++;
    }
    cout<<"cnt : "<<cnt<<"\n";
    printfall(n);


    return 0;
}


반응형

'SW교육 > 알고리즘' 카테고리의 다른 글

알고리즘 체험  (0) 2015.12.27
그래프에서의 인접행렬과 인접리스트  (1) 2015.09.07
배열을 대신하는 벡터  (0) 2015.09.07