알고리즘 모음(C++)

2021 충북 컴퓨터 꿈나무 축제 기출문제 - 중등부(도대회, 아두이노) 본문

아두이노 대회(충북)

2021 충북 컴퓨터 꿈나무 축제 기출문제 - 중등부(도대회, 아두이노)

공대생의 잡다한 사전 2022. 7. 20. 20:31

2021 충북컴퓨터꿈나무 축제 중등부 도대회 기출문제 답안입니다.

필수요소와 창의적문제 해결을 전부 구현했습니다.(LED를 FND로 대체했습니다)

문제 자체는 간단했지만 다이나믹 프로그래밍이라는 알고리즘을 사용해야했습니다.

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

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

www.acmicpc.net

문제에서 사용된 알고리즘은 해당 문제와 거의 유사했습니다. 따라서 해당 문제를 푸시고 기출문제를 보신다면 쉽게 풀 수 있습니다.

 

문제를 읽지 않으셨다면 아래의 파일을 통해 문제를 먼저 보시는 것을 추천합니다.

SW(중)2021(도대회).hwp
0.10MB

 

먼저 구현한 회로도입니다.

출력해야할 최대 점수가 몇자리가 될지 몰라 7세그먼트의 4digit을 사용했습니다.

4자리 FND의 연결 방법은 구글에 확인해보시면 나와있습니다.

스위치는 디지털핀의 2, 3번에 연결했으며 FND는 a~g는 4 ~ 10번, 자리핀은 A0 ~ A3에 연결했습니다.

 

문제의 핵심은 역의 최대 점수를 어떻게 구하냐입니다.

역의 최대 점수만 구하는 코드부터 보겠습니다.

void solve(){
	max_point[1] = station[1];
	max_point[2] = station[1] + station[2];
	for(int i = 3; i <= N; i++){
		max_point[i] = max(max_point[i-3] + station[i-1] + station[i], max_point[i-2] + station[i]);
     }
	cout << max_point[N];
}

해당 부분만 C++로 코드를 구현했습니다.

먼저 1번째 역의 경우 나올 수 있는 최대 점수는 자기 자신입니다. 따라서 자신의 포인트 값을 저장해주면 됩니다.

2번째 역 또한 마찬가지입니다. 1번 역과 2번 역의 합이 최대입니다.

3번 역부터 달라지는데, 연속된 3번의 역은 가지 못하기에 해당 역을 갈 수 있는 방법을 찾아야합니다.

1. 3번째 전의 역의 최댓값에서 현재 역과 바로 전 역의 값을 더한다. 

2. 2번째 전의 역의 최댓값에서 현재 역의 값을 더한다.

-> 2경우를 통해서 X번째 역의 최댓값을 구할 수 있습니다.

따라서 해당 코드를 활용한다면 문제를 쉽게 구현할 수 있습니다. 

 

 

다음은 시리얼 입력에 관한 코드입니다.

if(Serial.available()){ // 시리얼 입력이 들어왔을 때
        N = Serial.parseInt(); 
        while(Serial.available()){ // 입력 버퍼 무시
          Serial.read();
        } 
 }

먼저 Serial.available() 이라는 코드를 통해서 시리얼에서 입력이 들어왔는지를 확인합니다.

들어왔다면 참을 아니면 거짓을 return 해줍니다.

값이 들어왔다면 Serial.parseInt()를 통해 정수 값을 입력받을 수 있습니다.

이때 0이라는 추가 입력이 생깁니다.

따라서 해당 값을 무시하기 위해 while()문을 통해 쓸모없는 값들을 전부 빼줍니다.

 

 

토글 스위치 관련한 코드입니다.

void Sw_1(){ // 토글 스위치로 만들어준다
  buttonstate_1 = digitalRead(2);
  if(buttonstate_1 != lastbutton_1){
    if(buttonstate_1 == HIGH){
      cnt_1++;
    }
    delay(50);
  }
  lastbutton_1 = buttonstate_1;  
}

 

해당 문제에서 스위치 입력을 눌렀을 때 한번만 인식하도록 해주는 것이 구현하기에 편합니다.

따라서 해당 코드를 통해서 눌렀을 때 눌렀다는 것을 한번만 인식하도록 해줍니다.

 

 

4자리 FND의 경우는 1자리와는 다르게 수를 표현할 때 몇번째 자리에 표시할 것인지를 정해줘야합니다.

FND 관련한 코드는 set_number(), pick_digit(), four_digit() 함수를 참고해주세요

 

제 코드는 변수를 통해 단계적으로 실행이 되게 구현했습니다.

따라서 코드의 실행 흐름을 따라가면서 주석을 참고해서 보시면 이해하실 수 있을 겁니다.

 

 

자세한 것은 코드를 참고해주세요

//////////////////////////////스위치 사용
int buttonstate_1 = 0;
int lastbutton_1 = 0;
int cnt_1 = 0;
int buttonstate_2 = 0;
int lastbutton_2 = 0;
int cnt_2 = 0;
///////////////////////////////
int station[50] = {0,}; //각 역의 점수
int max_point[50] = {0,}; // 각 역의 최대 점수
int cnt_station[50] = {0,}; // 각 역에서 들린 역의 갯수
String name_station[50]; // 들린 역의 이름
int N = 0;     // 역의 갯수
int stage = 0; // 함수들이 유기적으로 작동할 수 있도록 변수값을 정한다.
////////////////////////////// FND 4자리 사용
int digit_pin[4] = {A0, A1, A2, A3}; // FND 1~4번째 자리
int num_pin[7] = {4,5,6,7,8,9,10};  // FND a~g까지 연결된 핀 번호
int digit_scale[4] = {1,10,100,1000}; // 각 자릿수를 나타낸다
int digits[10][7] = { // 0~9까지 FND를 나타내는 방법
  {1,1,1,1,1,1,0}, // 0
  {0,1,1,0,0,0,0}, // 1
  {1,1,0,1,1,0,1}, // 2
  {1,1,1,1,0,0,1}, // 3
  {0,1,1,0,0,1,1}, // 4
  {1,0,1,1,0,1,1}, // 5
  {1,0,1,1,1,1,1}, // 6
  {1,1,1,0,0,0,0}, // 7
  {1,1,1,1,1,1,1}, // 8
  {1,1,1,0,0,1,1}  // 9
};
 
void setup() {
  pinMode(2, INPUT); //SW
  pinMode(3, INPUT); //SW
  for(int i = 0; i < 7; i++) pinMode(num_pin[i], OUTPUT); // FND 핀
  for(int i = 0; i < 4; i++) pinMode(digit_pin[i], OUTPUT); // FND 자리
  clear_fnd(); //처음에 FND를 지우고 시작
  Serial.begin(9600);
}
 
void clear_fnd(){ // FND를 전부 지운다
  for(int i = 0 ; i < 4; i++) digitalWrite(digit_pin[i], 1); 
}
 
void set_number(int n){ // FND a ~ g 까지 어떤 신호를 통해 키고 끌지를 정한다
  for(int i = 0; i < 7; i++) digitalWrite(num_pin[i], digits[n][i]);  
}
 
void pick_digit(int n){ // 몇번째 자리 FND를 선택할지 정한다
  digitalWrite(digit_pin[n], 0);
  delay(1); // delay가 있어야지 fnd가 선명하게 보임  
}
 
void four_digit(int n){ // FND에 어떤 수를 킬지 정한다
  for(int i = 0; i < 4; i++){
    set_number(n/digit_scale[i]%10);
    pick_digit(i);
    clear_fnd();  
  }  
}
 
void Sw_1(){ // 토글 스위치로 만들어준다
  buttonstate_1 = digitalRead(2);
  if(buttonstate_1 != lastbutton_1){
    if(buttonstate_1 == HIGH){
      cnt_1++;
    }
    delay(50);
  }
  lastbutton_1 = buttonstate_1;  
}
 
void Sw_2(){
  buttonstate_2 = digitalRead(3);
  if(buttonstate_2 != lastbutton_2){
    if(buttonstate_2 == HIGH){
      cnt_2++;
    }
    delay(50);
  }
  lastbutton_2 = buttonstate_2;  
}
 
void input_station(){ // 역의 점수를 입력 받는 코드
  Serial.println("각 역의 값을 입력하시오");
  int x = 1;
  while(1){
    if(Serial.available()){ // 시리얼 입력이 들어올때 
      station[x] = Serial.parseInt();
      x++; //다음 역 점수로 넘어간다
    }
    if(x > N){ // N개의 점수가 모두 들어왔을 경우
      while(Serial.available()){ // 입력 버퍼 무시
        Serial.read();
      }
      break;
    }
  }
  Serial.println("각 역의 점수는 ");
  for(int i = 1; i <= N; i++){
    Serial.print(station[i]);  
    Serial.print(" ");
  }
  Serial.println("입니다");
}
 
void get_station_cnt(){ // 몇개의 역을 입력받을지 정하는 코드
  Serial.println("몇개의 역이 존재하는지 입력하시오");
    while(1){
      if(Serial.available()){ // 시리얼 입력이 들어왔을 때
        N = Serial.parseInt(); 
        while(Serial.available()){ // 입력 버퍼 무시
          Serial.read();
        } 
      }
      if(N > 0){ // 역의 갯수가 입력이 됐으니
        break;
      }
    }
    stage = 1; // 다음 스테이지로 넘어간다 -> 함수들이 유기적으로 작동하게 한다
    input_station(); 
}
 
void get_max_point(){ //최댓값, 들린 역의 갯수, 들린 역의 정보를 구하는 코드
  max_point[1] = station[1]; // 1번째 역의 최댓값은 자기 자신이다.
  cnt_station[1] = 1;        // 들린 역이 1개이다.
  name_station[1] = "1 ";    // 1번 역을 들렸음으로 해당 문자열 저장
  max_point[2] = station[1] + station[2]; // 2번째 역의 최댓값은 1번과 2번역을 들린 경우이다.
  cnt_station[2] = 2;
  name_station[2] = "1 2 ";
  for(int i = 3; i <= N; i++){ //1번과 2번 역은 구했음으로 3번 역부터 시작
    int sum_1 = max_point[i-3] + station[i-1] + station[i]; // 올 수 있는 첫번째 경우는 i-3번째에서 i-1, i번째 역을 거쳐온 경우 
    int sum_2 = max_point[i-2] + station[i];                // 올 수 있는 두번째 경우는 i-2번째에서 i번째 역을 거쳐온 경우다.
    if(sum_1 > sum_2){                                      // 두 경우 중 큰 경우를 찾는다.
      max_point[i] = sum_1;
      cnt_station[i] = cnt_station[i-3] + 2; 
      name_station[i] = name_station[i-3] + (i-1) + " " + (i) + " ";  
    }
    else if(sum_1 < sum_2){
      max_point[i] = sum_2;
      cnt_station[i] = cnt_station[i-2] + 1;  
      name_station[i] = name_station[i-2] + (i) + " "; 
    }
    else{ //갈 수 있는 경우가 값이 같은 경우 더 적은 역을 들린 것을 가져온다
      max_point[i] = sum_1;
      if(cnt_station[i-3] + 2 > cnt_station[i-2] + 1){
        cnt_station[i] = cnt_station[i-2] + 1; 
        name_station[i] = name_station[i-2] + (i) + " "; 
      }
      else{
        cnt_station[i] = cnt_station[i-3] + 2;  
        name_station[i] = name_station[i-3] + (i-1) + " "+ (i) + " "; 
      }
    } 
  }
  Serial.print("최고 점수는 ");
  Serial.print(max_point[N]);
  Serial.println(" 입니다.");
  delay(100);
  Serial.print("들린 역의 갯수는 ");
  Serial.print(cnt_station[N]);
  Serial.println(" 입니다.");
  delay(100);
  Serial.print("들린 역은 ");
  Serial.print(name_station[N]);
  Serial.println("입니다.");
}

void loop() {
  Sw_1(); // 토글 스위치를 사용 
  Sw_2();
  if(cnt_1 > 0 && stage == 0){ // 점수를 입력 받는 스위치를 눌렀을 경우
    get_station_cnt();
  }
  if(stage == 1){ // 역의 갯수를 입력 받았다면
     get_max_point(); 
     for(int i = 0; i < 500; i++){ // 최댓값을 약 0.5초동안 출력 
      four_digit(max_point[N]);
      delay(1); 
     }
     for(int i = 0; i < 500; i++){ // 들린 역의 갯수를 약 0.5초동안 출력
      four_digit(cnt_station[N]);
      delay(1); 
     }
     stage = 2;
  }
  if(cnt_2 > 0 && stage == 2){ //초기화 버튼을 눌렀을 경우
    Serial.println("다음달 데이터로 넘어갑니다.");
    Serial.println("/////////////////////////////////");
    cnt_1 = 0; // 사용된 모든 변수를 초기화 함으로써 처음부터 작동하도록 한다. 배열을 초기화를 안해도 된다.
    stage = 0;
    N = 0;
    cnt_2 = 0;
  }
}

 

질문이 있으시다면 댓글을 남겨주세요