Date:

1) 문제 링크

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

2) 접근

맨 처음엔 1을 생성자로 해서 10000 이하인 항들까지 쭉 출력하는 아주 쉬운 문제로 생각했다. 근데 아니었쥬?
그 다음으로 접근한 건 수학적 증명을 이용한 함수를 만드는 것. 셀프 넘버라면 반드시 만족시키는 수학적 성질이 있을 것이고 이를 이용하면 아주 짧은 시간 내에 풀 수 있을 것이라 생각했다. 일단 1자리, 2자리, 3자리, 4자리인 경우로 나눠서 시작했는데… 문자가 잔뜩 있는 부정방정식만 나와서 걍 노답이라 생각하고 일단 접어뒀다.
그 다음 생각한 건 단순 노가다. 1부터 9999까지 생성자로 수를 만들고, 크기가 10000인 체크 배열을 만들어 셀프 넘버가 아닌 걸 지워나가는 방식이다.

3) 아이디어 + 풀이

마지막에 생각한 아이디어 그대로 구현해봤다. 처음 짠 코드는 이렇다.(C++11)

#include <iostream>
using namespace std;

int check[10001];
// 셀프 넘버 아닌 수 체크
int CheckNotSelfNum(){
    int di, temp; // num 넣어서 나오는 항 값, i값 임시저장소
    // d(i) 값 계산 후 check 배열 인덱스 대입 하여 체크
    for(int i=1; i<=10000; ++i){
        di = i;
        temp = i;
        while(temp){
            di += temp%10;
            temp /= 10;
        }
        if(di <= 10000) ++check[di];
    }
}

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    CheckNotSelfNum();
    // 셀프 넘버 출력
    for(int i=1; i<=10000; ++i)
        if(!check[i]) cout << i << '\n';

    return 0;
}

이렇게 넣고 돌리니 시간 제한 1초인데도 TLE가 떴다. 엥?? 뭐… 넉넉하게 잡아도 4X10000+10000 해서 50000번 밖에 안 돌아가는데 1초에서 TLE?? 이번에는 똑같은 시간 복잡도지만 수열 함수만 따로 빼서 구현했다.(C++11)

#include <iostream>
using namespace std;
// 수열 함수 d(n)
int d(int n){
    int res = n, temp = n;
    while(temp){
        res += temp%10;
        temp /= 10;
    }
    return res;
}

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int check[10001]={0, };
    int dn;

    // 셀프 넘버 아닌 수 체크
    for(int i=1; i<=10000; ++i){
        dn = d(i);
        if(dn <= 10000) ++check[dn];
    }
    // 셀프 넘버 
    for(int i=1; i<=10000; ++i){
        if(!check[i]) cout << i << '\n';
    }

    return 0;
}

그랬더니 TLE 안뜨고 실행시간 0ms 떴다 ㅋㅋㅋㅋㅋ 왜 그러냐 대체

4) 피드백 + 다른 정답들

CheckNotSelfNum() 만들어서 돌릴 때랑 d(n) 만들어서 돌릴 때랑 무슨 차이가 있는지 알아봐야 겠다. 그리고 n<d(n)이 성립함을 이용하면 for문 2개 쓸 필요도 없음을 깨달았다. 그냥 처음 for문에 출력문까지 넣어버려도 된다.

Leave a comment