문제 링크


소수와 관련된 다른 문제를 풀기위해 자세히 알아보게된 에라토스테네스의 체이다. 에라토스테네스의 체란 빠르게 소수들을 구하는 방법이다. 즉 특정수 n 이하의 모든 소수를 구하고 싶으면, n이하의 수중 합성수들을 지워나가면, 남은 숫자들은 소수가 된다는 것에서 착안한다.

먼저 2 이상의 가장 작은 수중, 아직 지우지 않는 수를 찾는다.  아직 하나도 지우지 않았으므로 2를 선택한다. 2는 소수이다, 그럼 2의 배수들은 전부 합성수들이므로, 2의 배수들은 모두 지워나간다.

그다음에 지워지지 않은 가장 작은 수는 3이다. 3은 소수이고, 3의 배수들은 전부 합성수이므로 3의 배수는 전부 지워나간다. 숫자를 다 지울때까지 반복한다.

이게 바로 에라토스테네스의 체이다. 





빠른 이해를 위한 이미지. 출처 위키피디아


이 문제는 에라토스테네스의 체처럼 소수를 구하는 문제와는 거리가 멀고, 에라토스테네스의 체가 어떤 순서로 숫자들을 지워나가고, 원하는 순서의 숫자를 구하는 문제이다. 그냥 이는 에라토스테네스의 체를 구현한 다음 해당 번째에서 출력하고 종료하면 되는 문제였다.

밑은 해당 소스코드이다.


#include <cstdio>
int n,k,a[1001]={0,},i,count=0;
int main(){
    scanf("%d %d",&n,&k);
    while(count!=k){
        //지우지 않은 가장 작은 수를 찾는다. 
        for(i=2;i<=n;i++)
            if(a[i]==0) break;
        //다 찾았는데 i가 1000이라면 다 지웠으므로 탈출한다. 
        if(i==1000) break;
        //i번째 수를 1을 채워 지웠음을 표시하고, 차례로 그 배수를 지워나간다. 
        for(int j=1;j*i<=n;j++){
            //만약 지우지 않은 숫자이면 해당 숫자 카운트를 증가시킨다. 
            if(a[i*j]==0){
                a[i*j]=1;
                count++;
            }
            if(count==k){
                printf("%d",i*j);
                return 0;   
            }
        }
    }
}


'Algorithm > Problems' 카테고리의 다른 글

알고스팟 - NQUEEN, 백준 - 9663 N-QUEEN  (1) 2016.02.25
알고스팟 - MORSE, DICT  (0) 2016.02.25
백준 - 1963 소수경로  (0) 2016.02.24
백준 - 1541 잃어버린 괄호  (0) 2016.02.23
백준 - 1946 신입사원  (0) 2016.02.23


문제 링크


greedy 문제이다. 풀이는 간단하다. 괄호 개수의 제한이 없기 때문에 '-'를 만나면, 다음 '-'를 만날 때까지 그 값들을 전부 더한 뒤 계산 값에서 빼주면 된다.

왜냐하면 괄호 앞에 '-'가 있을 경우, 분배 법칙으로 괄호 안의 양수 값들은 전부 '-'로 처리되기 때문이다. 

나는 처음에 '-'를 토큰으로 문자열을 전부 나눈 뒤, 전부 덧셈 연산 처리를 한 다음에 빼줘서 풀었다.

하지만 그럴 필요 없이 그냥 값들을 전부 더해주다가 '-'를 만나면 그때부터 전부 다 빼주면 된다. 어차피 첫 '-'뒤의 '+'들은 전부 분배 법칙으로 '-'가 될 것이며, 다음 '-'를 만나면 그 전에 괄호를 닫고 다시 열게 되기 때문이다.


우선 처음에 그냥 '-'를 기준으로 토큰을 나눈 뒤 풀었던 코드이다. 나눈 토큰 들을 '+'처리한 다음에 첫 문자열을 제외한 나머지 문자열들은 전부 빼줬다.

//-가 나오면, 그 때부터 다음 -가 나올때까지 전부다 괄호를 쳐버리면 된다.
//-를 토큰으로 문자열을 추출한다. 그 문자열 안에 있는 값들의 +연산을 모두 처리하면 된다. 
#include <cstdio>
#include <cstring>
#include <cstdlib>
char cal[51] = { 0, };
char ca[51][51] = {0,};
char* ptr;
int ret,i=0;
//+연산을 처리하는 함수 
int calculate(char* str) {
    int r = 0;
    char* ptr2;
    ptr2 = strtok(str, "+");
    r += atoi(ptr2);
    while (ptr2 = strtok(NULL, "+")) {
        r += atoi(ptr2);
    }
    return r;
}
int main() {
    scanf("%s", cal);
    //-를 토큰으로 분해하고, 각 문자열을 저장한다. 
    ptr = strtok(cal, "-");
    strcpy(ca[i++],ptr);
    while (ptr = strtok(NULL, "-")) {
        strcpy(ca[i++],ptr);
    }
    //분해한 문자열을 전부 +연산 처리를 하고, 첫 수를 제외한 나머지 수는 전부 뺄셈 처리를 한다. 
    ret=calculate(ca[0]);
    for(int j=1;j<i;j++){
        ret -= calculate(ca[j]);
    }
    printf("%d",ret);
}


이 코드만 봐도 알고 보면 첫 '-'이후의 값들은 전부 다 빼주면 된다는 걸 알 수 있다.

다음 코드는 이를 적용해 보았다.


#include <cstdio>
#include <cstring>
#include <cstdlib>
char cal[51] = { 0, };
char ca[51] = {0,};
char* ptr;
int ret,i=0;
//덧셈을 처리하는 함수. 전과 달라진건 -,+을 안가리고 전부 더해버린다. 
int calculate(char* str) {
    int r = 0;
    char* ptr2;
    ptr2 = strtok(str, "+-");
    r += atoi(ptr2);
    while (ptr2 = strtok(NULL, "+-")) {
        r += atoi(ptr2);
    }
    return r;
}
int main() {
    scanf("%s", cal);
    //첫 -를 기준으로 토큰을 나눈다. 
    ptr = strtok(cal, "-");
    strcpy(ca,ptr);
    //첫 - 이후의 문자열을 받는다. 
    ptr = strtok(NULL,"");  
    ret=calculate(ca);
    //첫 - 이후의 문자열은 전부 덧셈 처리한 뒤 빼준다. 
    if(ptr!=NULL)ret-=calculate(ptr); 
    printf("%d",ret);
}

마지막으로는 다른 분 코드를 참조해 짜본, 라이브러리 함수를 쓰지 않고 배열을 하나하나 검사해서 짜는 코드이다.


#include <cstdio> char cal[51] = { 0, }; int ret=0,n=0,i=0,flag=0; int main() { scanf("%s", cal); while(cal[i] != 0){ //숫자이면 if(cal[i] >='0' && cal[i]<='9'){ //이전에 저장한 숫자의 자릿수를 하나 늘려주고 이 값을 더해준다. n*=10; n+=cal[i]-'0'; } //그 후 지금까지 숫자값들을 0으로 초기화해준다. else{ //-연산자를 만난적이 없으면, 즉 flag가 0이면 그냥 더해준다. if(flag==0) ret +=n; //-연산자를 만난적이 있으면, 즉 flag가 1이면 뺄셈처리한다. else ret-=n; //첫 -이면, 즉 -이고 flag가 0이면 flag를 1로 만들어준다. if(cal[i]=='-'&&flag==0) flag=1; n = 0; } i++; } //마지막 숫자를 flag에 맞게 처리해준다. if(flag==0) ret+=n; else if(flag==1) ret-=n; printf("%d",ret); }


'Algorithm > Problems' 카테고리의 다른 글

알고스팟 - NQUEEN, 백준 - 9663 N-QUEEN  (1) 2016.02.25
알고스팟 - MORSE, DICT  (0) 2016.02.25
백준 - 1963 소수경로  (0) 2016.02.24
백준 - 2960 에라토스테네스의 체  (1) 2016.02.24
백준 - 1946 신입사원  (0) 2016.02.23


문제 링크


greedy로 푸는 문제이다.
내가 풀이한 방법은 먼저 서류 등수나 면접 등수를 기준으로 오름차순 정렬을 한다
자신을 제외한 다른 모든 신입 사원과 점수를 비교해서, 무조건 적어도 하나의 항목은 다른 인원보다 높아야 하므로 두 항목 중 하나라도 1등인 지원자는 무조건 선발되기 때문에 1등에 대한 카운트 하나를 증가시켜준다. 만약 서류 등수를 기준으로 오름차순 정렬을 했으면, 서류 등수를 따라 올라가면서, 처음에 서류 1등이 가진 면접 등수보다 등수가 높은 인원을 찾는다. 만약 찾았다면 그 지원자는 서류 1등보다 서류 등수는 낮지만 면접 등수는 높으므로 선발 자격이 된다.
그리고 기준을 방금 찾은 지원자의 면접 등수로 설정하고, 그 새로 기준을 잡은 면접 점수보다 높은 사람을 찾아나간다. 애초 서류 등수 기준으로 오름차순을 했으므로, 그 뒤에 현재 기준의 면접 점수보다 등수가 높은 사람의 서류 등수는 무조건 그 전에 찾은 지원자보다 낮지만 면접 점수는 높게 되기 때문에 선발 조건을 만족할수밖에 없다. --> 즉, 정렬한 이후에는 최장증가부분수열을 찾으면 된다.

이렇게 로직 생각을 하고 처음에 코드를 작성하였다. 

#include <cstdio> #include <algorithm> int t,n,rank[100000][2],r1,r2; int recruit(){ //정렬한다. for(int i = 0; i<n; i++){ for(int j= 1;j<n-i;j++){ if(rank[j][0]<rank[j-1][0]){ std::swap(rank[j][0],rank[j-1][0]); std::swap(rank[j][1],rank[j-1][1]); } } } int min = rank[0][1],count = 1;; for(int i=0;i<n;i++){ if(min>rank[i][1]){ count++; min = rank[i][1]; } } return count; } int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); //각 사원의 등수 입력 for(int i =0; i<n;i++){ scanf("%d %d",&rank[i][0],&rank[i][1]); } printf("%d\n",recruit()); } }


정렬은 그냥 편하게 버블 소트로 하였더니 시간 초과가 발생하였다. 이를 해결하려고 퀵소트로 바꿔서 다시 정렬을 해보았으나. 이 또한 시간 초과가 발생하였다. 

그러면 입력을 받은 후 정렬을 하는 것이 아니라, 입력 도중에 정렬을 할 필요가 있었다. 고민하던 차에 ,이 문제에서 애초에 '1위부터 N위까지 동석차 없이 결정된다고 가정'했으므로, 그냥 등수에 맞는 배열의 인덱스에다가 저장하면 될 일이었다. 


#include <cstdio>
using namespace std;
int t,n,rank[100001],rx,ry;
int recruit(){
    int min = rank[1],count = 1;
    for(int i=1;i<=n;i++){
        if(min>rank[i]){
            count++;
            min = rank[i];
        }
    }   
    return count;
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        //각 사원의 등수 입력 
        for(int i =0; i<n;i++){
            //서류점수 등수 순서대로 면접 점수를 입력받는다. 
            scanf("%d %d",&rx,&ry);
            rank[rx] = ry; 
        }
        printf("%d\n",recruit());
    }
}


그렇게 코드를 다시 작성하였다. 다른 사람들의 코드를 보니 이 방법이나, 정렬 코드를 짜는 것이 아닌 STL의 sort를 통한 정렬은 시간 초과 없이 작동하는 것 같았다.

아마 테스트케이스중에 완전 역순으로 정렬된 테스트케이스가 있어 정렬에 n^2이 걸렸기 때문인 것 같았다. 이 경우에 대한 처리나 머지소트로 정렬했으면 시간초과가 안날것 같기는 하다.

어려운 문제는 아니었지만 너무 틀에 박힌 생각보다는 좀 더 폭넓게 생각하는 버릇이 필요한 것 같다는 생각이 든다..


'Algorithm > Problems' 카테고리의 다른 글

알고스팟 - NQUEEN, 백준 - 9663 N-QUEEN  (1) 2016.02.25
알고스팟 - MORSE, DICT  (0) 2016.02.25
백준 - 1963 소수경로  (0) 2016.02.24
백준 - 2960 에라토스테네스의 체  (1) 2016.02.24
백준 - 1541 잃어버린 괄호  (0) 2016.02.23

+ Recent posts