2022 KAKAO BLIND RECRUITMENT-양궁대회-C++

고동현·2024년 5월 12일
0

PS

목록 보기
26/51

양궁대회 바로가기

이번문제는 완전탐색으로 풀수있는 문제였다.
핵심을 알면 쉬웠던 문제로, 만약 10점에서 이길꺼면 어피치보다 딱 1발 더쏴서 이기고 질꺼면 아에 안쏘는게 이득이다.

한번 풀어보자.

일단 어떤식으로 접근해야할까?
문제를 처음 볼때 쓱 보면서 이해를 하고 뒤에 이어지는 제한조건을 보면서 n값을 본다.
제한조건인 n이 10발이하이고, 점수대는 0점~11점으로 고정이다.

이렇게 n값이 너무 이상하지 않으면, 일단 시간복잡도를 고려하지 않고, 어떻게 풀지 로직을 짠다.

그 다음에 대충 대충 로직을 짜면 그 로직에 제한조건을 대입했을때 가능한지 확인해보는거다.

라이언입장에서 봐보자, 일단 라이언 입장에서는 점수대마다 이기거나 지는경우밖에 없다. 왜냐하면 어피치와 같은 화살을 쐈으면 어피치가 점수를 가져가기 때문이다.

그러면 0점부터 10점까지 경우의수는 각 점수당 이기거나 지는 경우이므로 2^11일것이다. 이러면 2048인데 여기서 하나의 lose...lose 의 벡터에서 for문으로 각 점수대마다 돌아야하니까 11번을 돌더라도
2^11 곱하기 11 곱하기 3 니까 1억번이내에 돌수있겠다 생각하고 완탐으로 풀어도 되겠다고 생각하는것이다.

그니까 대충 로직을 생각해보고 n값을 대입해서 시간복잡도안에 해결할 수 있는지 파악하라는것이다. 만약에 로직대로했는데 값이 너무 크면 다른방법을 생각해야한다.

어쨋든 그러면 경우의 수는
ffff..f
ffff..t
fffff.tf
등 2^11의 경우를 먼저 만들어야한다.
필자는 Backtracking방법으로 만들었다.

경우의수만들기

vecotr<int>v;

void BT() {
    if (v.size() == 10) {
        mycheck();
        return;
    }

    for (int j = 0; j < 2; j++) {
        if (j == 0) {
            v.push_back(0);
            BT();
            v.pop_back();
        }
        else {
            v.push_back(1);
            BT();
            v.pop_back();
        }
    }
}

그다음에 size가 10이면 mycheck를 통해서 확인한다.
근데 왜 점수는 11개인데 size가 10일까 바로
vector v는
0번 index부터
10점 9점...순으로 이어지니까 마지막은 index9번에 1점이다.
0점은 누가 이기던지 상관없이 점수가 늘어나지 않으므로 1점까지만센다.->이렇게하면 경우의 수가 최소 1048개는 줄어듬

mycheck

void mycheck() {
    int arrow = 0;
    int r_score = 0;
    vector<int>shoot;
    int a_score = 0;
    for (int i = 0; i < 10; i++) {
        if (v[i]) {
            arrow += (info[i] + 1);
            shoot.push_back(info[i] + 1);
        }
        else {
            shoot.push_back(0);
        }
    }
    ,,,

for문으로 v를 돌면서 이기면 arrow에다가 어피치가 쏜 화살수보다 1더해서 더하고, shoot 즉 내가 쏜 화살인 vector에다가 넣는다.
만약 지는경우면 아에 쏘지 않는다.

이렇게 해야하는 이유는 10점에 3발쏜다고 30점을 얻는게아니라 몇발을 쏘든 어피치보다 많이 쏘면 딱 10점만 얻어가는구조이기 때문이다.
그렇다면 질꺼면 아에 안쏘는게 맞다.
왜냐하면 낮은점수에 많이 쏜 답을 return해야하므로 화살이 남으면
남은 화살을 모두 0점에 박으면 되기 때문이다.

 //라이언 어피치 점수계산
    for (int i = 0; i < 10; i++) {
        if (info[i] < shoot[i]) {
            r_score += (10 - i);
        }
        else if (info[i] > shoot[i]) {
            a_score += (10 - i);
        }
        else if (info[i] == shoot[i] && info[i] != 0) {
            a_score += (10 - i);
        }
    }

그 다음에 이제 info와 shoot을 바탕으로 점수계산을해준다.

//0점에다 남은 화살 전부 박기
    if (r_score > a_score && arrow <= n) {
        shoot.push_back(n - arrow);
      
        if(mymax<r_score-a_score){
            answer.clear();
            answer=shoot;
            mymax = r_score-a_score;
        }else if(mymax==r_score-a_score){

        for (int i = 10; i >= 0; i--) {
            if ((shoot[i]>0 || answer[i]>0) && shoot[i] > answer[i]) {
                answer.clear();
                answer = shoot;
                break;
            }
            else if ((shoot[i] > 0 || answer[i] > 0) && shoot[i] < answer[i]) {
                break;
            }
        }
        }
    }
}

그다음 마지막로직인데 ryan의 점수가 appeach의 점수보다 높은데
화살을 n개 범위내에 쐈다면,
마지막인 0점에다가 남은 화살을 박는게 이득이니까 shoot.push_back(n - arrow); 이렇게 값을 넣어준다.

그다음 최대 점수차이로 이기는게 답이니까 최대 점수차이인지 확인하고
만약, 최대 점수차이라면,
for문을 돌면서 낮은점수에서 answer보다 많이 쐈다면 answer를 갱신해준다.
여기서 핵심은 vector의 index 0 번은 점수로는 10점이므로
거꾸로 0점에 해당하는 index 10부터 확인해야한다는것이다.

정답코드

#include <string>
#include <vector>
#include <iostream>
using namespace std;
vector<int>v;
vector<int> answer;
vector<int> info;
int n;
int mymax = 0;
void mycheck() {
    int arrow = 0;
    int r_score = 0;
    vector<int>shoot;
    int a_score = 0;
    for (int i = 0; i < 10; i++) {
        if (v[i]) {
            arrow += (info[i] + 1);
            shoot.push_back(info[i] + 1);
        }
        else {
            shoot.push_back(0);
        }
    }

    //라이언 어피치 점수계산
    for (int i = 0; i < 10; i++) {
        if (info[i] < shoot[i]) {
            r_score += (10 - i);
        }
        else if (info[i] > shoot[i]) {
            a_score += (10 - i);
        }
        else if (info[i] == shoot[i] && info[i] != 0) {
            a_score += (10 - i);
        }
    }

    //0점에다 남은 화살 전부 박기
    if (r_score > a_score && arrow <= n) {
        shoot.push_back(n - arrow);
      
        if(mymax<r_score-a_score){
            answer.clear();
            answer=shoot;
            mymax = r_score-a_score;
        }else if(mymax==r_score-a_score){

        for (int i = 10; i >= 0; i--) {
            if ((shoot[i]>0 || answer[i]>0) && shoot[i] > answer[i]) {
                answer.clear();
                answer = shoot;
                break;
            }
            else if ((shoot[i] > 0 || answer[i] > 0) && shoot[i] < answer[i]) {
                break;
            }
        }
        }
    }
}

void BT() {
    if (v.size() == 10) {
        mycheck();
        return;
    }

    for (int j = 0; j < 2; j++) {
        if (j == 0) {
            v.push_back(0);
            BT();
            v.pop_back();
        }
        else {
            v.push_back(1);
            BT();
            v.pop_back();
        }
    }
}
vector<int> solution(int N, vector<int> Info) {
    n = N;
    info = Info;
    for (int i = 0; i < 11; i++) {
        answer.push_back(0);
    }
    BT();
    int c=0;
    for(int i=0;i<answer.size();i++){
        if(answer[i]==0) c++;
    }
    if(c==11){
        answer.clear();
        answer.push_back(-1);
    }
    return answer;
}
profile
항상 Why?[왜썻는지] What?[이를 통해 무엇을 얻었는지 생각하겠습니다.]

0개의 댓글