그 동안 알고스팟의 '왕초보'문제를 풀어보았습니다.

퇴근하고 집에 와서 졸린 눈 부비며 풀기에는 '왕초보'도 벅찬 경우가 많았습니다.

코딩은 정말 정신이 또렷한 상태에서 해야지,

졸음코딩이나 음주코딩을 하면 효율을 내기가 힘든 작업이란걸 다시 깨달았습니다.


오늘부터는 '초보' 문제들을 하나씩 풀어보도록 하겠습니다.

튜토리얼에 '초보' 난이도로 총 26문제가 명시되어 있습니다.

하루에 한 문제씩 풀다보면 한달이 훌쩍 지나가 있겠네요.



- 제목 : URI

- 난도 : ☆☆☆☆☆ (0점/5점)

- 문제

   (간단히 의역해보겠습니다) URI(Uniform Resource Identifier)는 인터넷 공간에서 리소스를 확정할 때 사용하는 간단한 스트링입니다.

    http://storycompiler.com

    mailto:storycompiler@storycompiler.com

    ftp://127.0.0.1/main.php

    README.txt

    위의 URI를 인터넷으로 보낼때 몇몇 특수문자는 %인코딩으로 변환해야 합니다.

    %인코딩은 %와 두자리의 16진수로 구성됩니다.

    예를 들자면, " "(빈칸)은 %20, "!"은 %21, "$"은 %24 등으로 정의됩니다.

    %인코딩된 문자열을 본래 문자열로 변환하는 프로그램을 작성하세요.

- 답안

#include <stdio.h>

int main(int argc, char **argv)
{
    int total_count = 0;
    int count = 0;

    scanf("%d", &total_count);

    for (; count < total_count; count++) {
        char string[81] = {0, };
        char tostring[81] = {0, };
        register int i = 0;
        register int j = 0;

        scanf("%s", string);

        while (string[i]) {
            if (string[i] == '%') {
                char special = '0';
                if (string[i + 1] != '2') {
                    i += 3;
                    continue;
                }

                switch (string[i + 2]) {
                case '0':
                    special = ' ';
                    break;
                case '1':
                    special = '!';
                    break;
                case '4':
                    special = '$';
                    break;
                case '5':
                    special = '%';
                    break;
                case '8':
                    special = '(';
                    break;
                case '9':
                    special = ')';
                    break;
                case 'a':
                    special = '*';
                    break;
                default:
                    break;
                }
                tostring[j] = special;
                i += 3;
                j++;
            } else {
                tostring[j] = string[i];
                i++;
                j++;
            }
        }
        printf("%s\n", tostring);
    }

    return 0;
}


- 문제에 특화하여 코딩하였습니다.

   %인코딩에 첫번째 숫자가 '2'외에도 많겠지만,

   문제에서 제한한 숫자는 '2'이기에 if 문을 하나 두어 '2'인 경우에만 처리하도록 하였습니다.


- while (string[i]), 오랜만에 while문을 사용하였습니다.

   경우에 따라 i++ 혹은 i += 3처럼 사용해야해서 for문 대신 while문으로 i값을 직접 컨트롤하였습니다.


- switch - case문은 커널 코딩스타일에 따라 같은 열에 배치되도록 하였습니다.

   case문은 switch보다 한 탭 아래에 두는 경우도 많긴 한데요,

   커널에서는 탭을 매우 제한적으로 사용하도록 하고 있기 때문에 switch - case에서는 탭을 아끼는 정책을 택했습니다.

   탭을 제한적으로 사용했던 이유는 예전 터미널이 80열까지만 지원했기 때문이지요.


이상으로 알고스팟에서 총 10문제를 풀었습니다.

순위는 1284위로 상승하였습니다.

머지않아 1000위 내로도 올라가겠군요.


그럼 좋은 하루 보내세요~

끝_

단위를 환산하는 문제는 매우 쉬운 편에 속합니다.

초등학교때 배운 비례식이 결국 단위환산이겠지요.


- 단위를 제대로 파싱하는지

- 그리고 자릿수가 제대로 출력하는지

위에서 실수만 하지 않는다면 정답을 도출할 수 있을겁니다.



- 제목 : CONVERT

- 난도 : ☆☆☆☆☆ (0점/5점)

- 문제 : 단위를 변환하세요.

                 킬로그램 : 파운드 = 0.4536 : 1.0000 =   1.0000 : 2.2046

                 볼륨 : 갤런 = 1.0000 : 0.2642 = 3.7854 : 1.0000   

- 답안

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    register int n = 0;
    int count = 0;

    scanf("%d", &count);

    for (; n < count; n++) {
        double number = 0.0f;
        char unit_old[4] = {0, };
        char *unit_new = NULL;

        scanf("%lf %s", &number, unit_old);

        if (!strcmp(unit_old, "kg")) {
            number *= 2.2046;
            unit_new = "lb";
        } else if (!strcmp(unit_old, "lb")) {
            number *= 0.4536;
            unit_new = "kg";
        } else if (!strcmp(unit_old, "l")) {
            number *= 0.2642;
            unit_new = "g";
        } else {
            number *= 3.7854;
            unit_new = "l";
        }

        printf("%d %.4lf %s\n", n + 1, number, unit_new);
    }

    return 0;
}


- scanf()에서 double 타입은 %lf로 받아야합니다.

  f는 float이지만, d는 이미 integer가 사용하고 있기 때문에,

 double에게는 다른 문자를 할당해야했지요.

  f 앞에 l을 붙여서 long float의 약어인 %lf를 사용합니다.


- printf()에서 double 타입은 %lf로 받습니다.

  하지만, %lf 대신 %f를 사용해도 동작에는 차이가 없습니다.

  그래서 굳이 %f로 사용하는 사람들도 있습니다.

  왜 그럴까요?우선 prototype부터 알아보시죠.


- Prototype이란?

  함수에서 prototype이란 함수의 argument를 의미합니다.

  함수를 선언&정의할 때 prototype을 명시할 수도 있고 그렇지 않을 수도 있습니다.


- Prototype을 명시한 경우

/* prototype과 선언하는 경우 */
void func(char a, short b, float c);

/* prototype과 정의하는 경우 */
void func(char a, short b, float c)
{ /* ... */ }


- Prototype을 명시하지 않는 경우

/* prototype 없이 선언하는 경우 */
void func();

/* prototype 없이 정의하는 경우 : K&R의 "오래된" C Style */
void func(a, b, c)
    char a;
    short b;
    float c;
{ /* ... */ }

/* 가변인수를 사용하는 경우 */
void func(int a, ...);


- Prototype이 명시되어 있는 함수에 argument를 넘겨주는 것은 아무런 문제가 없습니다.

 char는 char형으로, short는 short형으로, float는 float형으로 전달됩니다.

 반면, prototype이 명시되어 있지 않는 함수에서 argument는 그대로 넘겨주지 않습니다.

 char, short는 integer로 자동변환되고, float는 double로 자동변환됩니다(아래 C99 규약).

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

  unsigned char와 unsigned short는 unsigned int로 자동변환되겠죠.


- printf()는 가변인자를 사용하고 있기 때문에 prototype이 명시되어있지 않은 함수입니다.

  따라서 printf()에 인자로 float를 넣어주면 자동변환에 의해 double로 바뀝니다.

  printf() 내부에서는 float에 대한 처리를 할 필요가 없게 됩니다.

  무조건 double 형이 들어오기 때문이지요.

  따라서 "%f"나 "%lf"나 똑같은 취급을 받습니다.

  물론 개인적으로는 가독성을 위해 "%lf"를 사용하는게 낫다고 생각합니다.


- 그렇다면, 왜 default argument promotion을 하는 것일까요?

  1988년 이전의 "dark age" 시절에는 함수 prototype이 없었습니다.

  그렇기 때문에 컴파일러 입장에서는,

  함수 아규먼트로 char나 short를 입력해도 함수정의의 타입인지 확신할 수 없었습니다.

  그래서 레지스터(4바이트)에 넣을만한 크기로 각각의 인자를 promotion해서 전달하였습니다.

  1바이트 char나 2바이트 short를 4바이트 int로 promotion한다고 추가 비용이 없습니다.

  어차피 4바이트 단위로 데이터가 관리되기 때문이지요.

  이렇게 넘겨받은 변수는 정의된 함수에서 캐스팅하여 사용했습니다.

  

- 개인적인 생각입니다만 컴파일한 파일을 메모리에 올려서 사용할 때,

  함수를 사용하는 쪽에서 float 타입의 0010 0001을 넘겨주었지만,

  함수정의에서 double로 패러미터를 정의했다면,

  0000 0000 0010 0001처럼 앞에 4바이트가 더 붙어야합니다.

  함수를 사용하는 쪽에서 미리 공간을 마련하여 스택에 쌓지 않으면,

  받는 쪽에서 캐스팅하여 사용할때 공간을 추가해야하니 고려해야할게 많았겠지요.

  그래서 관리의 용이함을 위해,

  미리 double만큼의 공간을 잡고 거기서 float를 사용하는게 쉽지 않았을까 싶기도 합니다.

  (물론 지금 설명은 dark age 기준의 설명입니다. 현대의 컴파일러 기준이 아닙니다.)


- 이상으로 printf()에서 %lf를 사용한 이유를 설명하였습니다.


- printf()에서 소수점 자릿수를 표현하려면 ".n"(여기서 n은 숫자)를 사용합니다.

  문제에서는 소수점 4자리로 출력을 해야했기 때문에 "%.4f"를 사용했습니다.

  ".4"를 사용하면 소수점이 없더라도 ".0000"이 붙게 됩니다.


오늘을 알고리즘 자체보다는,

"%.4f"를 설명하기 위해 더 공을 드린 것 같습니다. :)


이로써 알고스팟에서 총 8문제를 풀었습니다.

순위는 1617위가 되었습니다.


그럼 좋은 하루 보내세요.

끝_


* 튜토리얼

[algospot] 알고스팟에서 HELLOWORLD 문제풀기


* 왕초보급 구현문제

[algospot] 알고스팟, '왕초보' 난이도 ENDIANS 풀기

[algospot] 알고스팟, '왕초보' 난이도 DRAWRECT 풀기

[algospot] 알고스팟, '왕초보' 난이도 LECTURE 풀기

[algospot] 알고스팟, '왕초보' 난이도 ENCRYPT 풀기

요즘은 퇴근하고 집에서 빈둥거리다가 자기 직전에 알고스팟에 마실나갑니다.

무언가 적극적으로 캐내어 공부를 한다기 보다는,

제법 쉬운 문제들을 풀며 잊어버린 것들을 떠올리는데 의미를 찾고 있습니다.

나름 천편일률적인 일상에서 소소한 재미였는데...



알고스팟에 접속이 안됩니다.

거의 매일같이 들락날락거렸는데 그간 본 적이 없는 에러였습니다.


구글이나 네이버에서 알고스팟과 관련된 접속에러 기록을 찾아봤지만 따로 나오는 것은 없네요.

그 동안 사이트 접속불가와 관련된 문제상황은 별로 없었나 봅니다.


전통적으로 "502 Bad Gateway"는 서버단에서 발생하는 문제를 의미합니다.

서버에 접속하려고 시도하는 데스크탑의 문제로 발생하진 않습니다.

다시 말하면, 아무리 컴퓨터를 재부팅하고 알고스팟에 접속하려 해도 같은 에러메시지를 볼 확률이 무지 높다는 것입니다.


"502 Bad Gateway"가 일어나는 대다수의 이유는,

서버가 오버로드되었기 때문입니다.

이 경우는 약간의 시간을 두고 F5 키를 눌러서 refresh하면 웹페이지가 다시 읽을 수 있습니다.

하지만, F5를 눌러도 웹페이지가 제대로 읽히지 않는다면 다른 가능성을 고려해봐야합니다.



1


우선 브라우져를 바꿔봅시다.

익스플로러에서만 실행했다면 크롬에서도 실행해봅니다.

크롬에서만 실행했다면 익스플로러에서도 실행해봅니다.

브라우져에 따라 지원 피쳐가 다르기 때문에,

한 브라우져에서 먹통이라도 다른 브라우져에서는 잘 나올 수 있습니다.

하지만, 오늘의 알고스팟은 어느 브라우져에서 접근해도 502 에러를 내뿜습니다.



2


그렇다면 다음으로는 내게만 그런지 아니면 다른 곳에서도 그런지 가려내야 합니다.

http://downforeveryoneorjustme.com/

위의 사이트에 접속하여 "algospot.com"을 입력해봅니다.



위처럼 입력하고 엔터를 칩니다.

그러면 모든 사람이 제대로 접근할 수 없는지 아니면 나만 그런건지 확인할 수 있습니다.

하지만, 결과는 놀랍게도 저만 접근할 수 없다고 나옵니다.



정말 저만 접근할 수 없는걸까요?

우분투 피씨에서도 접속해봅니다.

안됩니다.

폰으로도 접속해봅니다.

안됩니다.

아마 서버에서 주는 데이타가 있으면 접속이 된다고 판단하는 것으로 보입니다.

서버에서 "502 Bad gateway"라는 정보를 던져주고 있으니까요.



3


다음으로 시도할 수 있는 것은 웹기반의 프록시 서버를 이용하는 겁니다.

ISP에서 알고스팟으로 접근하는 것을 막았을 수도 있습니다.

그럴 경우 알고스팟으로 직접 접근하지 않고 웹프록시서버를 이용하여 간접접근을 할 수 있습니다.

http://anonymouse.org/anonwww.html

위의 사이트 중간 "Enter website address:"에 알고스팟 주소를 적습니다.



위처럼 적고,

"Surf anonymously" 버튼을 누릅니다.

잠시 후 아래처럼 결과가 나왔습니다.



에러라고 합니다.

간접접근도 안된다면 적어도 ISP 등에 의해 접근차단이 된 것은 아닙니다.



4


그 다음으로 시도해볼 것은 브라우져에 캐시된 데이터를 삭제하는 것입니다.

아무 드문 케이스로 캐시된 데이터로만 화면을 구성하는 경우가 있습니다.

만약 캐시된 데이터가 "502 Bad gateway" 뿐이라면 에러메시지만 출력할 수 있습니다.

하지만, 이런 문제일리는 거의 없습니다.


여기까지 시도해보고도 안되면,

컴퓨터를 재부팅하고 다시 시도합니다.

오늘의 알고스팟은 그래도 접속이 안되네요.


그냥 잠이나 자야겠습니다. :)

그럼 좋은 하루 보내세요~


끝_


  1. 아침에 접속하니 잘 됩니다. 사이트에서 로그를 확인해보니 4시간정도 먹통이었네요.

왕초보 세번째 문제를 풀어보기로 합니다.

이번 문제까지 패스하면 이제 삼천대에서 이천대로 순위가 상승하겠네요.


이 포스팅을 보고 계실 분들과도 함께 풀어보고 싶습니다.

한가로운 오후를 무료하게 보내고 계시다면,

함께 알고리즘을 소재로 열혈코딩하시죠~



- 제목 : DRAWRECT

- 난도 : ☆☆ (0점/5점)

- 문제 : 직사각형의 3개 점이 주어졌을때 다른 한 점을 유추하세요.

- 답안

#include <stdio.h>

int main(int argc, char **argv)
{
    register int i = 0;
    int count = 0;

    scanf("%d", &count);

    for (; i < count; i++) {
        int x[2] = {0, };
        int y[2] = {0, };
        int j = 0;
        int tx = 0;
        int ty = 0;

        for (; j < 3; j++) {
            scanf("%d %d", &tx, &ty);

            if (x[0] == 0) x[0] = tx;
            else if (x[0] == tx) x[0] = 0;
            else if (x[1] == 0) x[1] = tx;
            else if (x[1] == tx) x[1] = 0;
           
            if (y[0] == 0) y[0] = ty;
            else if (y[0] == ty) y[0] = 0;
            else if (y[1] == 0) y[1] = ty;
            else if (y[1] == ty) y[1] = 0;

        }

        printf("%d %d\n", x[0] + x[1], y[0] + y[1]);
    }

    return 0;
}

좌표평면 상에서는 x축 2개, y축 2개로 직사각형을 표현할 수 있습니다.

문제를 간단하게 보기 위해 우선 x축 먼저 생각해보기로 하지요.

x축 2개에 총 4개의 점을 배정해야 합니다.

각 축은 점 2개씩 가지고 있게 되겠죠.

점이 3개가 주어졌다면, 2개는 같은 축이고 다른 하나는 알려지지 않은 점과 같은 축이겠지요.

따라서 3개의 점을 분석하여 중복한 축은 그냥 0으로 제거해버리고 중복되지 않은 값만 남깁니다.


x와 마찬가지로 y도 진행합니다.

y축도 역시 2개의 축을 대상으로 하기 때문에 2개의 저장공간만 있으면 됩니다.

아래와 같은 자료형만 사용하면 문제를 풀 수 있습니다.


위의 문제로 총 4문제를 풀었습니다.

순위는 2626위로 올랐습니다.

아직은 워낙 낮은 순위이다 보니 한 문제만 풀더라도 순위는 비약적으로 올라가네요.


그럼 좋은 하루 보내세요~

끝_



+ Recent posts