728x90
728x90

문제 링크 : www.acmicpc.net/problem/20055

 

20055번: 컨베이어 벨트 위의 로봇

길이가 N인 컨베이어 벨트가 있고, 길이가 2N인 벨트가 이 컨베이어 벨트를 위아래로 감싸며 돌고 있다. 벨트는 길이 1 간격으로 2N개의 칸으로 나뉘어져 있으며, 각 칸에는 아래 그림과 같이 1부

www.acmicpc.net

 

 

 

 

문제

길이가 N인 컨베이어 벨트가 있고, 길이가 2N인 벨트가 이 컨베이어 벨트를 위아래로 감싸며 돌고 있다. 벨트는 길이 1 간격으로 2N개의 칸으로 나뉘어져 있으며, 각 칸에는 아래 그림과 같이 1부터 2N까지의 번호가 매겨져 있다.

벨트가 한 칸 회전하면 1번부터 2N-1번까지의 칸은 다음 번호의 칸이 있는 위치로 이동하고, 2N번 칸은 1번 칸의 위치로 이동한다. i번 칸의 내구도는 Ai이다. 위의 그림에서 1번 칸이 있는 위치를 "올라가는 위치", N번 칸이 있는 위치를 "내려가는 위치"라고 한다.

컨베이어 벨트에 박스 모양 로봇을 하나씩 올리려고 한다. 로봇은 올라가는 위치에만 땅에서 올라가고, 내려가는 위치에서만 땅으로 내려갈 수 있다. 내려가는 위치에 로봇이 있는 경우 로봇은 반드시 땅으로 내려가야 한다. 로봇이 어떤 칸에 올라가거나 이동하면 그 칸의 내구도는 즉시 1만큼 감소한다. 내구도가 0인 칸에는 로봇이 올라갈 수 없다.

로봇은 컨베이어 벨트 위에서 스스로 이동할 수 있다.

컨베이어 벨트를 이용해 로봇들을 건너편으로 옮기려고 한다. 로봇을 옮기는 과정에서는 아래와 같은 일이 순서대로 일어난다.

  1. 벨트가 한 칸 회전한다.
  2. 가장 먼저 벨트에 올라간 로봇부터, 벨트가 회전하는 방향으로 한 칸 이동할 수 있다면 이동한다. 만약 이동할 수 없다면 가만히 있는다.
    1. 로봇이 이동하기 위해서는 이동하려는 칸에 로봇이 없으며, 그 칸의 내구도가 1 이상 남아 있어야 한다.
  3. 올라가는 위치에 로봇이 없다면 로봇을 하나 올린다.
  4. 내구도가 0인 칸의 개수가 K개 이상이라면 과정을 종료한다. 그렇지 않다면 1번으로 돌아간다.

종료되었을 때 몇 번째 단계가 진행 중이었는지 구해보자. 가장 처음 수행되는 단계는 1번째 단계이다.

입력

첫째 줄에 N, K가 주어진다. 둘째 줄에는 A1, A2, ..., A2N이 주어진다.

출력

몇 번째 단계가 진행 중일때 종료되었는지 출력한다.

제한

  • 2 ≤ N ≤ 100
  • 1 ≤ K ≤ 2N
  • 1 ≤ Ai ≤ 1,000

 

 


 

 

접근 방법

문제에서 주어진 조건대로만 잘 구현하면 되는 문제이다.

 

beltArr[1]에 로봇이 들어오고 beltArr[N]에서 로봇이 나가는 것에 유의하자.

 

내구도는 beltArr[1] ~ beltArr[2N] 에서 숫자가 돌고, 

로봇은 isRobot[1] ~ isRobot[N] 에서만 도는 것도 주의해야한다.

(처음에 로봇도 1부터 2N까지 돌려서 계속 헤맸었다.)

 

나머지는 주석을 참고하면 된다.

#include <iostream>
#include <utility>

using namespace std;

int beltArr[201] = {0};
pair<int,bool> isRobot[101]; //몇번째인지, 로봇 있는지여부
int N,K;

bool cntZero(){			//내구도 0인 곳이 K개 이상이면 true 리턴
    
    int cnt = 0;
    bool res = false;
    for(int i = 1; i<=2*N; i++){
        if(beltArr[i]==0){
            cnt++;
        }
        if(cnt>=K) {
            res = true;
            break;
        }
    }
    return res;
    
}

int findFirstRobot(){		//1~N 칸에서 가장 먼저 들어간 로봇 찾기
    
    for(int i = N; i>=1; i--){
        if(isRobot[i].second==true){
            return i;
        }
    }
    return 0;
}

//2번 : 로봇 한칸 전진 : 먼저 올라간 로봇부터 / 이동 가능하면(로봇 없고, 내구도 1이상) / 로봇&내구도 바꿈
void shiftRobot(){
    
    
    int minIdx = findFirstRobot();

    if(minIdx!=0){
        for(int i = minIdx; i>=1; i--){
            if(isRobot[i].second==true){    //현재 자리에 로봇이 있고
                
                if(i==N){					//마지막 로봇은 그냥 내림
                    isRobot[i].first = 0;
                    isRobot[i].second = false;
                }
                else{
                    if(isRobot[i+1].second==false && beltArr[i+1]>=1){  //다음 자리에 로봇 없고, 내구도 1 이상이면
                        isRobot[i+1] = isRobot[i];	//다음 칸으로 옮기고
                        beltArr[i+1] -= 1;
                        isRobot[i].first = 0;		//옮겼으니까 초기화
                        isRobot[i].second = false;
                    }
                }
                
            }
        }
    }
    
}
//1번 : 회전 : 내구도 & 로봇 위치 shift
void shiftNumber(){
    
    //내구도 이동
    int tmp = beltArr[2*N];
    for(int i = 2*N -1; i>=1; i--){
        beltArr[i+1] = beltArr[i];
    }
    beltArr[1] = tmp;
    
    
    //로봇 이동
    //내리는 위치의 로봇 처리
    isRobot[N].first = 0;
    isRobot[N].second = false;
    
    for(int i = N-1; i>=1; i--){
        isRobot[i+1] = isRobot[i];
    }
    isRobot[1].first=0;
    isRobot[1].second = false;
    
}

//3번 : beltArr[1]에 로봇없으면 로봇 하나 올림
bool putRobot(int order){
    
    if(beltArr[1]>=1 && isRobot[1].second==false) {	//처음칸의 내구도 1 이상이고 로봇 없으면
        beltArr[1]-=1;  //내구도 1 감소
        
        isRobot[1].first = order;
        isRobot[1].second = true;
        return true;
    }
    return false;
}

int solution(){
    
    int ord = 1;
    int step = 1;
    while(1){
        
        //한칸회전
        
        shiftNumber();
        if(cntZero()){break;}
        
        
        //먼저 올라간 로봇부터 전진
        shiftRobot();

        if(cntZero()){break;}
        
        //1번 비었으면 로봇 올림
        if(putRobot(ord)){		//로봇 올릴 수 있었으면 ord++
            ord++;
        }
        
        if(cntZero()){break;}
        step+=1;
    }
    
    return step; 
}

int main(){
    
    
    cin>>N>>K;
    
    for(int i = 1; i<=N; i++){
        scanf("%d", &beltArr[i]);
        isRobot[i].first = 0;
        isRobot[i].second = false;
    }
    for(int i = N+1; i<=2*N; i++){
        scanf("%d", &beltArr[i]);
    }
    
    cout << solution();
    
    return 0;
}

728x90
728x90

문제 링크 : www.acmicpc.net/problem/15662

 

15662번: 톱니바퀴 (2)

총 8개의 톱니를 가지고 있는 톱니바퀴 T개가 아래 그림과 같이 일렬로 놓여져 있다. 또, 톱니는 N극 또는 S극 중 하나를 나타내고 있다. 톱니바퀴에는 번호가 매겨져 있는데, 가장 왼쪽 톱니바퀴

www.acmicpc.net

 

 

문제

총 8개의 톱니를 가지고 있는 톱니바퀴 T개가 아래 그림과 같이 일렬로 놓여져 있다. 또, 톱니는 N극 또는 S극 중 하나를 나타내고 있다. 톱니바퀴에는 번호가 매겨져 있는데, 가장 왼쪽 톱니바퀴가 1번, 그 오른쪽은 2번, ..., 가장 오른쪽 톱니바퀴는 T번이다. 아래 그림은 T가 4인 경우이다.

이때, 톱니바퀴를 총 K번 회전시키려고 한다. 톱니바퀴의 회전은 한 칸을 기준으로 한다. 회전은 시계 방향과 반시계 방향이 있고, 아래 그림과 같이 회전한다.

톱니바퀴를 회전시키려면, 회전시킬 톱니바퀴와 회전시킬 방향을 결정해야 한다. 톱니바퀴가 회전할 때, 서로 맞닿은 극에 따라서 옆에 있는 톱니바퀴를 회전시킬 수도 있고, 회전시키지 않을 수도 있다. 톱니바퀴 A를 회전할 때, 그 옆에 있는 톱니바퀴 B와 서로 맞닿은 톱니의 극이 다르다면, B는 A가 회전한 방향과 반대방향으로 회전하게 된다. 예를 들어, 아래와 같은 경우를 살펴보자.

두 톱니바퀴의 맞닿은 부분은 초록색 점선으로 묶여있는 부분이다. 여기서, 3번 톱니바퀴를 반시계 방향으로 회전했다면, 4번 톱니바퀴는 시계 방향으로 회전하게 된다. 2번 톱니바퀴는 맞닿은 부분이 S극으로 서로 같기 때문에, 회전하지 않게 되고, 1번 톱니바퀴는 2번이 회전하지 않았기 때문에, 회전하지 않게 된다. 따라서, 아래 그림과 같은 모양을 만들게 된다.

위와 같은 상태에서 1번 톱니바퀴를 시계 방향으로 회전시키면, 2번 톱니바퀴가 반시계 방향으로 회전하게 되고, 2번이 회전하기 때문에, 3번도 동시에 시계 방향으로 회전하게 된다. 4번은 3번이 회전하지만, 맞닿은 극이 같기 때문에 회전하지 않는다. 따라서, 아래와 같은 상태가 된다.

톱니바퀴 T개의 초기 상태와 톱니바퀴를 회전시킨 방법이 주어졌을 때, 최종 톱니바퀴의 상태를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 톱니바퀴의 개수 T (1 ≤ T ≤ 1,000)가 주어진다. 

둘째 줄부터 T개의 줄에 톱니바퀴의 상태가 가장 왼쪽 톱니바퀴부터 순서대로 주어진다. 상태는 8개의 정수로 이루어져 있고, 12시방향부터 시계방향 순서대로 주어진다. N극은 0, S극은 1로 나타나있다.

다음 줄에는 회전 횟수 K(1 ≤ K ≤ 1,000)가 주어진다. 다음 K개 줄에는 회전시킨 방법이 순서대로 주어진다. 각 방법은 두 개의 정수로 이루어져 있고, 첫 번째 정수는 회전시킨 톱니바퀴의 번호, 두 번째 정수는 방향이다. 방향이 1인 경우는 시계 방향이고, -1인 경우는 반시계 방향이다.

출력

총 K번 회전시킨 이후에 12시방향이 S극인 톱니바퀴의 개수를 출력한다.

 

 

 


 

 

 

접근 방법

 

기준 톱니를 중심으로 좌,우를 탐색하면서 N,S극의 상태를 파악해 돌려야하는 톱니의 번호와 회전 방향을 queue에 넣는다.

큐에 넣어 마지막에 한번에 톱니 상태를 갱신해야 한다. 만약 그렇지 않고 오른쪽(또는 왼쪽) 으로 한칸씩 가면서 톱니를 회전시키면 해당 톱니의 N/S극 상태가 바뀌어서 다음 톱니 회전 여부에 영향을 주기 때문에 큐에 넣었다가 한번에 갱신하는 것이다.

 

//기준 톱니를 중심으로 왼쪽, 오른쪽 탐색하면서 돌려야하는 톱니바퀴들 큐에 넣음.
//탐색 끝나고 큐에 있는 톱니들 한번에 갱신(회전) 해야 서로 영향 안줌.
//톱니 하나씩 돌리면 N,S극 계속 바뀌어서 기존 톱니 상태 바뀌어버림

#include <iostream>
#include <vector>
#include <queue>
#include <utility>
using namespace std;

vector<int> arrT[1001];
int arrK[1001][2];

void turnRight(int idx){    //idx번째 톱니바퀴 시계방향으로 회전
    
    vector<int> vec = arrT[idx];
    int last = vec[7];
    for(int i=7; i>0; i--){
        vec[i] = vec[i-1];
    }
    vec[0] = last;
    arrT[idx].clear();
    arrT[idx] = vec;
    
}

void turnLeft(int idx){       //idx번째 톱니바퀴 반시계 방향으로 회전
    
    vector<int> vec = arrT[idx];
    int first = vec[0];
    
    for(int i = 0; i<7; i++){
        vec[i] = vec[i+1];
    }
    vec[7] = first;
    
    arrT[idx].clear();
    arrT[idx] = vec;
}

int main(){
    
    int T; cin>>T;
    for(int i = 1; i<=T; i++){
        vector<int> vec;
        for(int j = 0; j<8; j++){
            int n;
            scanf("%1d", &n);
            vec.push_back(n);
        }
        arrT[i] = vec;
    }
    
    int K; cin >> K;
    for(int i = 0; i<K; i++){
        for(int j=0; j<2; j++){
            cin >> arrK[i][j];
        }
        
    }

    queue<pair<int,int>> que;    //회전시켜야하는 톱니바퀴 push
    
    for(int i = 0; i<K; i++){
        
        int center = arrK[i][0];
        int turn = arrK[i][1];

        que.push(make_pair(center,turn));    //기준 톱니바퀴
        
        int right = T - arrK[i][0];     //오른쪽으로 탐색해야하는 톱니바퀴 개수
        int left = T-right-1;           //왼쪽으로 탐색해야하는 톱니바퀴 개수
        
        //1. 오른쪽 탐색
        for(int j = 1; j<=right; j++){
            
            if(arrT[center][2] != arrT[center+1][6]){   //달라서 회전해야할 경우
                turn*=-1;
                que.push(make_pair(center+1, turn));
            }
            else break;
            center++;
        }
        center = arrK[i][0];
        turn = arrK[i][1];
        
        //2. 왼쪽 탐색
        for(int j = 1; j<=left; j++){
            
            if(arrT[center][6] != arrT[center-1][2]){   //달라서 회전해야할 경우
                turn*=-1;
                que.push(make_pair(center-1, turn));
            }
            else break;
            center--;
        }

        while(!que.empty()){
            
            if(que.front().second==1) turnRight(que.front().first);
            else turnLeft(que.front().first);
            
            que.pop();
        }

    }
    

    int ans=0;
    for(int i = 1; i<=T; i++){
        if(arrT[i][0]==1) ans++;
    }
    
    cout << ans;
    
    return 0;
}

728x90
728x90

문제 링크 : www.acmicpc.net/problem/15990

 

15990번: 1, 2, 3 더하기 5

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.

www.acmicpc.net

 

 

문제

정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 3가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다. 단, 같은 수를 두 번 이상 연속해서 사용하면 안 된다.

  • 1+2+1
  • 1+3
  • 3+1

정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 100,000보다 작거나 같다.

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.

 

 

 


 

접근 방법

문제의 조건에서 유의해야 할 점이 있다.

 

1) 1+3과 3+1 이 다름.

2) 같은 수를 두 번 이상 연속해서 사용할 수 없음.

 

일단 규칙을 찾기 위해 쭉 써보았다.

 

dp[1] = 1 (1)  (단일 숫자)

dp[2] = 1 (2) (단일 숫자)

dp[3] = dp[2]에 1을 더하는 경우 (2+1 -> 1가지)

          + dp[1]에 2를 더하는 경우 (1+2 -> 1가지)

          + dp[0]에 3을 더하는 경우 (0+3 -> 1가지)

          = 1 + 1 + 1 =3 


dp[4] = dp[3]에 1을 더하는 경우 (1+2+1 , 0+3+1 -> 연속 제외하고 2가지)

          + dp[2]에 2를 더하는 경우 (연속 제외하고 0가지)

          + dp[1]에 3을 더하는 경우 (1+3 -> 1가지)

          = 2 + 0 + 1 = 3

 

dp[5] = dp[4]에 1을 더하는 경우 (1+3+1 -> 연속 제외하고 1가지)

          + dp[3]에 2를 더하는 경우 (2+1+2, 0+3+2 -> 연속 제외하고 2가지)

          + dp[2]에 3을 더하는 경우 (2+3 -> 1가지)

          = 1 + 2 + 1 = 4

 

dp[6] = dp[5]에 1을 더하는 경우 (2+1+2+1, 0+3+2+1, 2+3+1 -> 연속 제외하고 3가지)

          + dp[4]에 2를 더하는 경우 (1+2+1+2, 0+3+1+2, 1+3+2 -> 3가지)

          + dp[3]에 3을 더하는 경우 (2+1+3, 1+2+3 -> 연속 제외하고 2가지)

          = 3 + 3 + 2 = 8

 

dp[7] = dp[6]에 1을 더하는 경우 (1+2+1+2+1, 0+3+1+2+1, 1+3+2+1  / 2+1+3+1, 1+2+3+1 -> 연속 제외하고 5가지)

          + dp[5]에 2를 더하는 경우 (1+3+1+2, 2+3+2 -> 연속 제외하고 2가지)

          + dp[4]에 3을 더하는 경우 (1+2+1+3, 0+3+1+3 -> 연속 제외하고 2가지)

          = 5 + 2 + 2 = 9

 

 

dp[n] = arr1[n] + arr2[n] + arr3[n] 으로 분할해서 더한다고 하자. (RHS의 각 배열은 +1, +2, +3 하는 경우이다.)

 

그러면

arr1[n] = arr2[n-1] + arr3[n-1]

arr2[n] = arr1[n-2] + arr3[n-2]

arr3[n] = arr1[n-3] + arr2[n-3] 

인 규칙을 찾을 수 있다. (위에 색칠해놓은 글씨를 보면 이해하기 쉽다.)

 

 

 

참고

 

 

#include <iostream>

using namespace std;

long long dp[100001] = {0};
long long arr1[100001] = {0};
long long arr2[100001] = {0};
long long arr3[100001] = {0};


int main(){
    
        
    int TC; cin >> TC;

    arr1[3] = 1;    arr1[4] = 2;    arr1[5] = 1;
    arr2[3] = 1;    arr2[4] = 0;    arr2[5] = 2;
    arr3[3] = 1;    arr3[4] = 1;    arr3[5] = 1;
    
    dp[0] = 1; dp[1] = 1; dp[2] = 1;
    for(int i = 3; i<=5; i++) {dp[i] = (arr1[i] + arr2[i] + arr3[i]) % 1000000009;}
    
    for(int i = 6; i<=100000; i++){
        arr1[i] = (arr2[i-1] + arr3[i-1]) % 1000000009;
        arr2[i] = (arr1[i-2] + arr3[i-2]) % 1000000009;
        arr3[i] = (arr1[i-3] + arr2[i-3]) % 1000000009;
        dp[i] = (arr1[i] + arr2[i] + arr3[i]) % 1000000009;
    }
    
    
    for(int t = 0; t<TC; t++){
        
        int n;
        cin>>n;
        
        cout << dp[n] << "\n";
        
        
    }
    
    
    return 0;
}

 

주의할 점

int 형은 더할 때 범위가 넘어가므로 long long 자료형을 사용해야 한다.

그리고 for 문 안에서 arr1[], arr2[], arr3[] 을 구할 때도 Mod 연산을 적용해서 저장해야 한다. 

 

 

 

 

728x90
728x90

문제 링크 : www.acmicpc.net/problem/11723

 

11723번: 집합

첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.

www.acmicpc.net

 

문제

비어있는 공집합 S가 주어졌을 때, 아래 연산을 수행하는 프로그램을 작성하시오.

  • add x: S에 x를 추가한다. (1 ≤ x ≤ 20) S에 x가 이미 있는 경우에는 연산을 무시한다.
  • remove x: S에서 x를 제거한다. (1 ≤ x ≤ 20) S에 x가 없는 경우에는 연산을 무시한다.
  • check x: S에 x가 있으면 1을, 없으면 0을 출력한다. (1 ≤ x ≤ 20)
  • toggle x: S에 x가 있으면 x를 제거하고, 없으면 x를 추가한다. (1 ≤ x ≤ 20)
  • all: S를 {1, 2, ..., 20} 으로 바꾼다.
  • empty: S를 공집합으로 바꾼다. 

입력

첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다.

둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.

출력

check 연산이 주어질때마다, 결과를 출력한다.

 

 

 

 

 

접근 방법 1)

x의 범위가 1부터 20까지이니 크기가 21인 배열을 만들어서, S에 x 가 있으면 arr[x] = 1로 없으면 arr[x] = 0으로 표시했다.

cin이 M만큼 반복되므로 처음에는 시간 초과가 났으나 

 

ios_base::sync_with_stdio(0);

cin.tie(0);

 

을 추가해주니 시간초과가 해결 되었다.

#include <iostream>
#include <cstring>

using namespace std;


int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    string str = "";
    int M, x;
    int arr[21] = {0};
    
    
    cin >> M;
    for(int i = 0; i<M; i++){
        cin >> str;
        
        if(str=="add"){
            cin >> x;
            if(arr[x]==0){   //없으면
                arr[x] = 1;
            }
        }
        
        else if(str=="remove"){
            cin >> x;
            if(arr[x] == 1){  //있으면
                arr[x] = 0;
            }
            
        }
        else if(str=="check"){
            cin >> x;
            if(arr[x] == 0){   //없으면
                cout << "0\n";
            } else{
                cout << "1\n";
            }
        }
        else if(str=="toggle"){
           cin >> x;
            if(arr[x] == 1){  //있으면
                arr[x] = 0;
            } else{
                arr[x] = 1;
            }
        }
        else if(str=="all"){
            
            for(int k = 1; k<=20; k++){ arr[k] = 1;}
            
        }
        else if(str=="empty"){
            memset(arr, 0, sizeof(arr));
        }
    }
    
    return 0;
}

 

 

 

 

접근 방법 2)

해당 문제는 백준의 브루트포스 - 비트마스크 문제이다. 비트 마스크로도 문제를 풀어 보았다.

 

[비트마스크 정리]

s 에 숫자가 있으면 그 숫자 인덱스가 가리키는 비트를 1로 표시하는 방식으로 문제를 해결했다.

첫번째 그림의 1 : 1 << 1   (0000 0010)

두번째 그림의 2 : 1 << 2  (0000 0100)

두번째 그림의 3 : 1 << 3  (0000 1000)

 

위 그림에서는 나타낼 수 있는 숫자가 0~7 뿐이지만, s를 int형(4byte=32bit) 으로 잡았기 때문에 0~32 숫자가 s에 포함되는지 안 되는지 판단할 수 있고, 문제에서 x는 1~20이기 때문에 충분하다.

 

추가 : 입력한 숫자와 S를 or 연산 시킨다. 

삭제 : 입력한 숫자를 not 시키고 그 수를 S와 and 연산한다.

체크 : S에 입력한 숫자가 있으면,  둘을 and 연산 한 결과가 1일 것이다. S = {1,2} , check 1이면 

          S = 0000 0110

  (1<<1) = 0000 0010

      and = 0000 0010   

이므로 1번째 자리가 1이어서 if 조건절 안이 true 가 된다.

 

 

 

 

#include <iostream>
#include <cstring>

using namespace std;


//비트마스크로 풀기

int main(){
    
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    
    string str = "";
    int M, x;
    
    int S = 0;
   
    cin >> M;
    for(int i = 0; i<M; i++){
        cin >> str;
        
        if(str=="add"){
            cin >> x;
            S |= (1<<x);
        }
        
        else if(str=="remove"){
            cin >> x;
            S &= ~(1<<x);
        }
        else if(str=="check"){
            cin >> x;
            if(S & (1<<x)){   //있으면
                cout << "1\n";
            } else{
                cout << "0\n";
            }
        }
        else if(str=="toggle"){
           cin >> x;
            if(S & (1<<x)){  //있으면
                S &= ~(1<<x); //없애고
            } else{             //없으면
                S |= (1<<x);     //추가
            }
        }
        else if(str=="all"){
            S = (1<<21) - 1;
        }
        else if(str=="empty"){
            S = 0;
        }
        
    
    }
    
    return 0;
}

 

 

 

 

 

큰 차이는 나지 않는다. 

728x90
728x90

문제 링크 : www.acmicpc.net/problem/6588

 

6588번: 골드바흐의 추측

각 테스트 케이스에 대해서, n = a + b 형태로 출력한다. 이때, a와 b는 홀수 소수이다. 숫자와 연산자는 공백 하나로 구분되어져 있다. 만약, n을 만들 수 있는 방법이 여러 가지라면, b-a가 가장 큰

www.acmicpc.net

 

문제

1742년, 독일의 아마추어 수학가 크리스티안 골드바흐는 레온하르트 오일러에게 다음과 같은 추측을 제안하는 편지를 보냈다.

4보다 큰 모든 짝수는 두 홀수 소수의 합으로 나타낼 수 있다.

예를 들어 8은 3 + 5로 나타낼 수 있고, 3과 5는 모두 홀수인 소수이다. 또, 20 = 3 + 17 = 7 + 13, 42 = 5 + 37 = 11 + 31 = 13 + 29 = 19 + 23 이다.

이 추측은 아직도 해결되지 않은 문제이다.

백만 이하의 모든 짝수에 대해서, 이 추측을 검증하는 프로그램을 작성하시오.

입력

입력은 하나 또는 그 이상의 테스트 케이스로 이루어져 있다. 테스트 케이스의 개수는 100,000개를 넘지 않는다.

각 테스트 케이스는 짝수 정수 n 하나로 이루어져 있다. (6 ≤ n ≤ 1000000)

입력의 마지막 줄에는 0이 하나 주어진다.

출력

각 테스트 케이스에 대해서, n = a + b 형태로 출력한다. 이때, a와 b는 홀수 소수이다. 숫자와 연산자는 공백 하나로 구분되어져 있다. 만약, n을 만들 수 있는 방법이 여러 가지라면, b-a가 가장 큰 것을 출력한다. 또, 두 홀수 소수의 합으로 n을 나타낼 수 없는 경우에는 "Goldbach's conjecture is wrong."을 출력한다.

 

 

 

 

접근 방법

소수구하기 문제(nanyoungkim.tistory.com/32) 에서 나왔던 "에라토스테네스의 체"를 이용하여 n의 최대값까지 소수를 구한다.

 

a는 3부터 시작해서 홀수이기 때문에 2씩 키워가면서 a, b 가 소수인지 체크하여 조건에 맞게 출력한다.

 

이때 주의할 점은 시간초과 문제였다. 아래의 코드를 작성하니 시간초과 문제가 해결되었다. 

 

    ios_base::sync_with_stdio(false);

    cin.tie(NULL);

    cout.tie(NULL);

 

또한 메인함수의 for 문에서 a는 3부터 n/2까지 하면 더 시간을 단축시킬 수 있다.

 

 

#include <iostream>

using namespace std;


#define MAX 1000000

int primeArr[MAX] = {0};

void primeChk(){
    
    for(int i = 2; i*i<=MAX; i++){
        
        if(primeArr[i]==0){
        
            for(int j = i*i; j<=MAX; j+=i){
                primeArr[j] = 1;
            }
        }
        
    }
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    int n;
    primeChk();

    
    while(1){
        
        cin >> n;
        if(n==0){
            break;
        }
                   
        bool chk = false;
        
        for(int a = 3; a<= n; a+=2){
           // int b = n - a;
            if(primeArr[a]==0 && primeArr[n-a]==0) {    //a가
                cout << n << " = " << a << " + " << n-a << "\n";
                chk = true;
                break;
            }
        }
        if(!chk){
            cout << "Goldbach's conjecture is wrong.\n";
        }
    }
  
    
    return 0;
}

728x90
728x90

문제 링크 : www.acmicpc.net/problem/9095

 

9095번: 1, 2, 3 더하기

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

www.acmicpc.net

 

 

문제

정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 7가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다.

  • 1+1+1+1
  • 1+1+2
  • 1+2+1
  • 2+1+1
  • 2+2
  • 1+3
  • 3+1

정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 11보다 작다.

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

 

 

 

 

 

접근 방법

DFS 구조의 재귀로 풀었다. 

다른 보통의 문제들과 다른 점은 중복이 허용되기 때문에 visited[] 을 사용하지 않고 풀어야한다는 점이다.

 

테스트 케이스가 반복되니 테스트케이스 마지막에는 변수들을 초기화해주는 것을 잊지 말자.

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int n;
int num[3] = {1,2,3};
int cnt = 0;
vector<int> choiceVec;


void sumFunc(int sum){
    
    if(n<sum) return;
    
    if(n==sum){
        cnt++;
        return;
    }
    
    for(int i = 0; i<3; i++){
        
        sum+=num[i];
        choiceVec.push_back(num[i]);
        
        sumFunc(sum);
        choiceVec.pop_back();
        sum-=num[i];
        
    }
    
}


int main(){
    
    int T;
    cin >> T;
    for(int tc = 0; tc<T; tc++){
    
        cin >>n;
        sumFunc(0);
        cout << cnt << "\n";
        
        cnt = 0;
        choiceVec.clear();
        
    }
    
    
    return 0;
}
728x90
728x90

문제 링크 : www.acmicpc.net/problem/2309

 

 

2309번: 일곱 난쟁이

아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 키는 모두 다르며, 가능한 정답이 여러 가지인 경우에는 아무거나 출력한다.

www.acmicpc.net

 

문제

왕비를 피해 일곱 난쟁이들과 함께 평화롭게 생활하고 있던 백설공주에게 위기가 찾아왔다. 일과를 마치고 돌아온 난쟁이가 일곱 명이 아닌 아홉 명이었던 것이다.

아홉 명의 난쟁이는 모두 자신이 "백설 공주와 일곱 난쟁이"의 주인공이라고 주장했다. 뛰어난 수학적 직관력을 가지고 있던 백설공주는, 다행스럽게도 일곱 난쟁이의 키의 합이 100이 됨을 기억해 냈다.

아홉 난쟁이의 키가 주어졌을 때, 백설공주를 도와 일곱 난쟁이를 찾는 프로그램을 작성하시오.

입력

아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 키는 모두 다르며, 가능한 정답이 여러 가지인 경우에는 아무거나 출력한다.

출력

일곱 난쟁이의 키를 오름차순으로 출력한다. 일곱 난쟁이를 찾을 수 없는 경우는 없다.

 

 

 

 

 

접근 방법

재귀를 이용해서 풀었다. DFS와 같은 구조이다. 

9명 중 7명을 뽑아서 toPick이 0이 되었을 때 키를 모두 더해서 키의 합이 100이면 chk를 true로 바꾸서 탐색을 멈췄다.

문제에서 조건을 만족하는 경우 중 아무거나 출력하라 했으니 sum=100이 되는 경우를 찾았을 때 진행을 멈추는 것이 효과적일 것이다.

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


vector<int> heightVec;
bool isVisited[9] = {0};
bool chk = false;
vector<int> ansVec;
int ans = 0;

void Func(int sum, int toPick){
    
    if(toPick==0){
        
        if(sum==100){
            chk = true;
            return;
        }
  
    }
    
    for(int i = 0; i<9; i++){
        
        if(isVisited[i]==0){
            isVisited[i] = 1;
            ansVec.push_back(heightVec[i]);
            sum+=heightVec[i];
        
            Func(sum, toPick-1);
            if(chk){ break;}
            
            sum-=heightVec[i];
            ansVec.pop_back();
            isVisited[i] = 0;
        }
        
    }
    
}


int main(){
    
    
    for(int i = 0; i<9; i++){
        int h;
        cin>>h;
        heightVec.push_back(h);
    }
    
    sort(heightVec.begin(), heightVec.end());
    
    Func(0, 7);
    
    for(int i = 0; i<7; i++){
        cout << ansVec[i] << "\n";
    }
    
    
    return 0;
}

 

 

 

728x90
728x90

문제링크 : www.acmicpc.net/problem/1929

 

1929번: 소수 구하기

첫째 줄에 자연수 M과 N이 빈 칸을 사이에 두고 주어진다. (1 ≤ M ≤ N ≤ 1,000,000) M이상 N이하의 소수가 하나 이상 있는 입력만 주어진다.

www.acmicpc.net

 

문제

M이상 N이하의 소수를 모두 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 M과 N이 빈 칸을 사이에 두고 주어진다. (1 ≤ M ≤ N ≤ 1,000,000) M이상 N이하의 소수가 하나 이상 있는 입력만 주어진다.

출력

한 줄에 하나씩, 증가하는 순서대로 소수를 출력한다.

 

 

 

 

접근 방식 1 

 

페르마의 정리 이용ko.wikipedia.org/wiki/%EC%86%8C%EC%88%98%ED%8C%90%EB%B3%84%EB%B2%95#%ED%8E%98%EB%A5%B4%EB%A7%88_%EC%86%8C%EC%88%98%ED%8C%90%EB%B3%84%EB%B2%95

 

소수판별법 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 둘러보기로 가기 검색하러 가기 수론에서, 소수판별법은 어떤 자연수 N이 소수인지 합성수인지를 판별하는 알고리즘들을 말한다. 간단한 방법들[편집] 직접 나

ko.wikipedia.org

gcd(a,p)  = 1 , a<p and p가 소수이면 a^p-1 (mod p) 는 1과 합동이다.

 

그러나 a = 8, p = 9일때, 8^8 = 16777216이어서 이를 9로 나눈 나머지는 1인데, 9는 소수가 아니므로 예외가 발생한다.

 

#include <iostream>
#include <cmath>

using namespace std;

int gcd(int p, int a){
    
    int c;
    while(a!=0){
        c = p%a;
        p = a;
        a = c;
    }
    return p;
}

int main(){
    
    int M, N;
    cin >> M >> N;

    for(int p = M; p<=N; p++){

        for(int a = 2; a<p; a++){

            if(gcd(p,a) == 1){

                int chk =((int)pow(a,p-1)) % p;
                if(chk == 1){
                    cout << "a: " << a << " /p: " << p << endl;
                    break;
                }
            }
        }
    }

    return 0;
}

 

 

 

접근 방법 2

Miller-Rabin Primality Test 를 이용해보자. 

#include <iostream>
#include<cstdlib>
#include<ctime>
#include<cmath>


using namespace std;


bool Test(int a, int n){
    
    
    int u=0, t=0;
    int k = n-1;
    int tmp = k;
    if(k%2 != 0){   //k홀수 즉 n짝수(합성수 이므로 true return)
        return true;
    }
    
    while(k>=2){
        if(k%2 == 0) {
            t++;
            k /= 2;
        }
    }
    
    u = tmp / pow(2,t);
    
    int x0 = (int)pow(a,u) % n;
    int x=0;
    for(int i = 1; i<=t; i++){
        
        x = (x0*x0) % n;
        
        if(x==1 && x0 !=1 && x0!=n-1){
            return true;
        }
        
        x0 = x;
    }
    if(x!=1) return true;
    
    return false;

}


int main(){
    
    int M, N;
    cin >> M >> N;

   srand((unsigned int)time(NULL));

    for(int n = M; n<=N; n++){
        
        bool chk = true;
        for(int s = 0; s<10; s++){
            
            int a = rand();
            while(a>=n){a = rand();}
            
            if(Test(a,n) == true){
                //합성수
                chk = false;
                break;
            }
            
        }
        
        
        if(chk){ //s번 다 도는 동안 Test()가 모두 false 이면
            //n은 소수
            cout << n << endl;
        }
        
        
        
        
    }
    
    
    
    return 0;
}

시간 초과가 떴다.

 

 

 

 

 

 

 

 

접근 방법 3

 

많은 사람들이 선택한 '에라토스테네스의 체' 방법을 이용했다. 

참고 : ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4

 

에라토스테네스의 체 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 둘러보기로 가기 검색하러 가기 수학에서 에라토스테네스의 체는 소수를 찾는 방법이다. 고대 그리스 수학자 에라토스테네스가 발견하였다. 알고리즘[편집] 2

ko.wikipedia.org

 

 

 

2가 소수니까 2의 배수는 소수가 아니다. 그러므로 2의 배수를 쭉 지운다.

3도 소수니까 3의배수는 소수가 아니다. 그러므로 3의 배수를 쭉 지운다.

 

이때 주의할 점은 j의 시작값이다. i*j(j<i)까지는 이미 검사했으므로 j 시작값을 i*2 에서 i*i로 개선할 수 있다.

i=5, j=3일때를 예로 들어보자.

5*3 = 15까지는 이미 i=3일때 검사가 됐다. 그러므로 j시작 값을 10(소수 5의 첫번째 배수)이 아닌 25부터 할 수 있는 것이다. 

더 자세히 따져보기 위해 실제로 2~24의 수 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24  을 봐보자.

 

(i = 2) 2의 배수 4,6,8,10,12,14,16,18,20,22,24 를 checked 한다

(i = 3) 3의 배수 6,9,12,15,18,21,24 를 checked 하는데 사실 이 중 2의 배수들은 i=2일때 이미 checked된 상태이다.

(i = 4) 4의 배수 -> arr[4] = 1이므로 볼 필요도 없이 Pass

(i = 5) 5의 배수 10,15,20 -> 이미 checked된 상태이다.

 

그러므로 j시작값을 i*i 부터 할 수 있는 것이다. 그러면 중복을 제거할 수 있다.

 

#include <iostream>
#include<cstdlib>
#include<ctime>
#include<cmath>


using namespace std;

int arr[1000001] = {0};
int main(){
    
    
    int M, N;
    cin >> M >> N;

   
    arr[1] = 1; //1은 소수 아님
    
    
    
    for(int i = 2; i*i <= N; i++){
        
        if(arr[i]==0){
            for(int j = i*i; j<=N; j+=i){
                arr[j] = 1;
            }
        }
    }
    
    
    
    for(int i = M; i<=N; i++){
        if(arr[i] == 0 ) cout << i << "\n";
    }
        
    return 0;
}

 

728x90

+ Recent posts