비밀번호486 (PASS486) 도움 요청드립니다.

  • gloryof11
    gloryof11

    다시 질문방을 찾게 되었습니다;;

    소인수분해를 하면 약수의 갯수를 찾을 수 있을것 같아서 아래와 같이 프로그래밍을 했습니다.

    동작상 이상이 없는것 처럼 보이는데 2가지 문제가 있습니다;;

    1) 예제 입력의 3번째, 200 1000000 2000000 의 답이 19 가 아닌 12가 나옴
    2) 시간초과 발생

    혹시 제가 생각을 잘못하고 만든 코드 인지 답답하여, 문제 해결하신 분들과 고수님들 에게 조언을 부탁드립니다;;

    비밀번호 486

    #include <stdio.h>
    #include <time.h>
    
    int TC;
    unsigned long num,lo,hi;
    unsigned long cnt;
    
    unsigned long idx;
    unsigned long ARR[25];  // 2^24 = 16,777,216 , so 25 is enough as max idx.
    
    unsigned long pivot;
    unsigned long prime_factor;
    
    unsigned long result;
    
    int main(void)
    {
        scanf("%d",&TC);
    
        for(int i=0;i<TC;i++)
        {
            scanf("%lu %lu %lu",&num,&lo,&hi);
    
    clock_t start = clock();
    
            for(unsigned long j=lo;j<=hi;j++)
            {
                // init
                pivot = prime_factor = j;
                idx = 0;
                for(unsigned long m=0;m<25;m++)
                    ARR[m] = 0;
    
    
                // find prime factor
                for(unsigned long n=2;n<=pivot;n++)
                {
                    if(prime_factor%n==0) {
                        ARR[idx] = n;
                        idx++;
                        prime_factor = prime_factor/n;
                        n = 1;
                        pivot = prime_factor;
                    }
                    else
                    {
                        if(pivot/n+1 < n) continue; // bug fix
                        pivot = pivot/n+1;
                    }
                }
    
    
                // add last prime factor. upper code does not add last prime factor... ; bug fix
                if(prime_factor != 1) {
                    ARR[idx] = prime_factor;
                    idx++;
                }
    
    #if 0
                // for debugging
                if(idx == 1) {
                    printf("소수\n");
                    cnt = 2;
                }
                else {
                    for(int l=0;l<idx;l++) {
                        printf("%d ",ARR[l]);
                    }
                    printf("\n");
                }
    #endif
                // count all divisors
                // 1. sort
                for(unsigned long k=0;k<idx;k++)
                {
                    unsigned long temp = 0;
                    for(unsigned long k2=k;k2<idx;k2++)
                    {
                        if(ARR[k]>ARR[k2]) {
                            temp = ARR[k];
                            ARR[k] = ARR[k2];
                            ARR[k2] = temp;
                        }
                    }
                }
    
                // 2. sum of each prime factor * num
                unsigned long val = ARR[0];
                unsigned long each_cnt = 1;
                cnt = 1;
    
                for(unsigned long k3=1;k3<=idx;k3++)
                {
                    if(val == ARR[k3]) {
                        each_cnt++;
                    }
                    else {
                        cnt = cnt * (each_cnt+1);
                        val = ARR[k3];
                        each_cnt = 1;
                    }
                }
    
                if(cnt == num) result++;
    
            }
    
            printf("%lu\n",result);
    printf("time : %d\n",clock()-start);
    
            result = 0;
        }
    
        return 0;
    }
    

    10년 전
9개의 댓글이 있습니다.
  • hyunhwan
    hyunhwan

    소인수 분해를 하는 코드에서 n으로 나눠지지 않을 경우 pivot=pivot/n+1을 하시는데 이건 어떤 의도로 넣으신거죠?


    10년 전 link
  • gloryof11
    gloryof11

    특정수가 약수를 찾을때, a 라는 수가 소수인경우,1~a 까지 모두 계산하면 시간이 많이 걸릴것 같아 연산횟수를 줄여보고자 그렇게 하였습니다.
    3으로 나눠지지 않으면, a/3 ~ a 사이의 어떤수도 약수가 되지 않을 것 같다고 생각했습니다.


    10년 전 link
  • Being
    Being

    n이 2, 3, 4로 나누어지지 않는다면 n/24~n 사이에는 어떤 약수도 없나요?


    10년 전 link
  • gloryof11
    gloryof11

    의미가 좀 잘못 전달 된 것 같습니다. 다시 말씀드리면,
    n 이 2,3,4 로 나누어지지 않는다면, n/24(초과) ~ n(미만) 사이의 어떠한 수도 n 의 약수가 되지 못한다고 생각했습니다. (만약 약수가 있으면, 2,3,4 중 하나도 약수여야 하니까요.)


    10년 전 link
  • Being
    Being

    네 제가 맞게 이해한 것 같은데요.. 그래서 n = 25면 (25/24, 25) 사이의 어떤 수도 n의 약수가 아니라 보신 것 아닌가요?


    10년 전 link
  • hyunhwan
    hyunhwan

    그리고n 소인수분해 할때도 반복문을 2부터 \sqrt{n} 만큼만 돌려도 됩니다.


    10년 전 link
  • gloryof11
    gloryof11

    (Dear Being 님) n=25 인경우, 아래와같이 동작합니다.
    1. 2 는 약수아님. 그러므로 pivot = 25/2+1 = 13
    2. 3 은 약수아님. 그러므로 pivot = 25/3+1 = 9
    3. 4 는 약수아님. 그러므로 pivot = 25/4+1 = 7
    4. 5 는 약수임. (찾았다!)

    (제가 설명을 잘못드렸습니다;; pivot 은 항상 n 을 기준으로 변경이 되어야 합니다. 시간내어 관심 가져주셔서 감사드립니다!)


    10년 전 link
  • kcm1700
    kcm1700

    설명하신대로 작동하려면 pivot = pivot/n+1;이 아니라 pivot = j/n+1;이 되어야겠죠. 결국 \sqrt{j}까지 돌리는 것을 의도하신 것 같은데 pivot 없이 그냥 조건문을 n <= j/n으로 해주시면 됩니다.


    10년 전 link
  • gloryof11
    gloryof11

    pivot 이라는 변수를 부가적으로 사용하면서, 어려운 방법으로 소인수 분해를 하려 했었네요;; 도움주셔서 더 짧은 코드로 더 빠르게 정확히 소인수 분해를 할수 있게 되었습니다. 인제 시간 초과만 잡으면 해결 할 수 있을것 같습니다. 감사합니다!


    10년 전 link
  • 정회원 권한이 있어야 커멘트를 다실 수 있습니다. 정회원이 되시려면 온라인 저지에서 5문제 이상을 푸시고, 가입 후 7일 이상이 지나셔야 합니다. 현재 문제를 푸셨습니다.