-
Notifications
You must be signed in to change notification settings - Fork 2
21-Seol-Munhyeok #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
21-Seol-Munhyeok #75
Conversation
|
코테 시작한지 얼마 안됐을 때 푼 문제네요. 제 코드 보고 이해하고 지저분한 부분만 조금 도려내고 변수명만 수정했습니다! 문제를 푼지 오래되서 기억이 잘 안나지만, 화살을 효율적으로(?) 맞출 수 있다고 생각해서 저는 그리디로 처음에 접근한 것 같습니다. 사실 당시에는 그리디라고 생각하고 풀지는 않고 그냥 문제에 대한 최적 방법을 고민했던 것 같습니다. 따라서 dfs + 그리디 느낌으로 풀었습니다. 일반적인 dfs와 다른 점은 0점을 제외한 화살은 어피치보다 딱 1발만 더 쏘면 되기 때문에 바로 (어피치가 쏜 화살 + 1)만큼을 쏘면서 넘어간다는 점입니다. 이때 화살 개수가 부족할 수 있으므로 남은 화살을 체크해줘야합니다. 수고하셨습니다! 문제에 대해 여러 방안을 항상 생각하시는게 매우 인상깊으십니다!! 양궁 대회#include<bits/stdc++.h>
using namespace std;
vector<int> res;
int maxx = 0;
vector<int> av;
vector<int> rv(11, 0);
int n;
void func(int cnt) {
if(cnt == n) {
int sumA = 0;
int sumR = 0;
for(int i = 0; i < 11; i++) {
if(av[i] >= rv[i] ) {
if(av[i] != 0) sumA += 10 - i;
}
else sumR += 10 - i;
}
if(sumR > sumA && maxx <= sumR - sumA) {
maxx = sumR - sumA;
res = rv;
}
}
else {
for(int i = 0; i <= 10; i++) {
if(av[i] >= rv[i] && cnt + av[i] + 1 <= n) {
rv[i] += av[i] + 1;
func(cnt + av[i] + 1);
rv[i] = 0;
}
else if(i == 10) {
rv[10] += n - cnt;
func(n);
rv[10] = 0;
}
}
}
}
vector<int> solution(int cnt, vector<int> info) {
av = info;
n = cnt;
func(0);
if(maxx == 0) res = {-1};
return res;
}
|
카카오 기출 문제는 거의 다 풀어보신건가요? 이미 풀어보신 문제가 많네요. 대단하십니다. |
우연히 겹친 것 같습니다! 안풀어본게 대부분일거에요! |
|
주요 로직은 스무스하게 짠 것 같은데, 마지막 라이언이 가장 큰 점수 차이로 우승할 수 있는 방법이 여러 가지 일 경우, 가장 낮은 점수를 더 많이 맞힌 경우를 return 해주세요. 를 처리하는 부분에서 의외로 애를 먹었네요. 결국 반복문 돌면서 체크하는 방식으로 하드코딩했습니다. 뭔가 좋은 아이디어가 떠오를 법도 한데 또 이거보다 나은 로직이 있나..? 싶기도 하고요. 저는 그리디 관점에서 접근했고, 핵심 아이디어는 결국 점수는 0 ~10 사이에서만 얻을 수 있고, 이 하나의 점수에 대해 라이언이 가져가거나, 어피치가 가져가거나, 둘 다 못 가져간다 이 3가지 경우의 수 뿐이기에, 라이언이 0 ~ 10 사이에서 가져갈 수 있는 점수 목록을 전부 만든 뒤, 이 경우가 실제로 가능한지 를 체크했습니다. bool 배열을 만들어서 간단한 재귀를 통해 모든 경우의 수를 만든 후, 배열을 check 함수에 인자로 넘겨줍니다. // 라이언의 승리 점수 목록 뽑는 함수
void func(vector<bool> lion, int depth)
{
if(depth == 11)
{
check(lion);
return;
}
func(lion, depth + 1);
lion[depth] = true;
func(lion, depth + 1);
return;
}
그리고 라이언 점수, 어피치 점수를 반복문을 통해 구한 후 라이언의 점수가 더 큰 경우, 그 경우에서 기존 배열과의 비교를 통해 더 최소점을 많이 맞춘 경우를 확인한 후 answer를 갱신해 줍니다. 초기에 -1로 초기화해둔 후 조건에 맞지 않으면 갱신되지 않으니 자연히 -1이 return됩니다. // 점수차가 더 커서 갱신하는 경우
// 점수차가 더 크면 무조건 갱신
if(lionscore - peachscore > curdiff)
{
curdiff = lionscore - peachscore;
answer = lionarrow;
}
// 점수차가 같으면 낮은 점수부터 비교해서 더 많이 맞힌 쪽 선택
else if(lionscore - peachscore == curdiff)
{
for(int i = 10; i >= 0; i--)
{
if(lionarrow[i] > answer[i])
{
answer = lionarrow;
break;
}
else if(lionarrow[i] < answer[i])
{
// 기존 배열이 더 나음
break;
}
}
}
위에도 언급했지만, 최소점을 더 많이 맞춘 배열로 답을 가져가는 로직이 반복문 외에는 떠오르지 않네요... 좀 더 깔끔한 코드가 되었으면 하는데 여러모로 아쉬움이 남습니다. 카카오 코테 문제는 여러모로 구현력이 녹슬지 않게 되잡아주는 것 같아서 푸는 보람이 있네요. 아래는 제출 코드입니다. 문제 푸시느라 고생하셨어요! 양궁대회 / c++#include <bits/stdc++.h>
using namespace std;
vector<int> peachinfo;
int arrowcount;
vector<int> answer = {-1};
int curdiff = 0;
// func에서 뽑은 목록이 가능한지 체크 후 갱신
void check(vector<bool> lion)
{
int n = arrowcount;
int lionscore = 0;
int peachscore = 0;
vector<int> lionarrow(lion.size(),0);
for(int i = 0; i < lion.size(); i++)
{
// 라이언이 이긴 점수
if(lion[i])
{
lionarrow[i] += peachinfo[i] + 1;
n -= peachinfo[i] + 1;
lionscore += (10 - i);
}
// 피치가 이긴 점수
else
{
if(peachinfo[i] > 0)
{
peachscore += (10 - i);
}
}
}
// 남은 화살은 전부 0점으로
if(n > 0)
{
lionarrow[10] += n;
}
// 지거나 무승부
if(lionscore <= peachscore)
{
return;
}
// 화살 n개보다 더 많이 사용
if(n < 0)
{
return;
}
// 갱신하는 경우
// 점수차가 더 크면 무조건 갱신
if(lionscore - peachscore > curdiff)
{
curdiff = lionscore - peachscore;
answer = lionarrow;
}
// 점수차가 같으면 낮은 점수부터 비교해서 더 많이 맞힌 쪽 선택
else if(lionscore - peachscore == curdiff)
{
for(int i = 10; i >= 0; i--)
{
if(lionarrow[i] > answer[i])
{
answer = lionarrow;
break;
}
else if(lionarrow[i] < answer[i])
{
// 기존 배열이 더 나음
break;
}
}
}
}
// 라이언의 승리 점수 목록 뽑는 함수
void func(vector<bool> lion, int depth)
{
if(depth == 11)
{
check(lion);
return;
}
func(lion, depth + 1);
lion[depth] = true;
func(lion, depth + 1);
return;
}
vector<int> solution(int n, vector<int> info) {
arrowcount = n;
peachinfo = info;
vector<bool> lion(11,false);
func(lion,0);
return answer;
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우와 이것도 쉽지는 않네요
보자마자 완전탐색, 그리디, DP 모두 떠올렸지만
점수 구간은 11개뿐이라 각 구간에서 “그냥 포기하기” 또는 “어피치보다 한 발 더 쏘기” 두 가지 선택을 고려하면 최대 2¹¹ 정도라 충분히 전부 탐색 가능할 것 같아서 바로 완전 탐색 DFS로 갔습니다.
문제 가져와주셔서 감사하고, 문제 푸느라 고생하셨습니다~!
양궁대회
#include <vector>
#include <algorithm>
using namespace std;
static bool betterTie(const vector<int>& a, const vector<int>& b) {
for (int i = 10; i >= 0; i--) {
if (a[i] != b[i]) {
return a[i] > b[i];
}
}
return false;
}
static void dfs(int idx,
int arrowsLeft,
int lion,
int apeach,
const vector<int>& info,
vector<int>& cur,
vector<int>& best,
int& bestDiff) {
if (idx == 11) {
cur[10] += arrowsLeft;
int diff = lion - apeach;
if (diff > 0) {
if (diff > bestDiff) {
bestDiff = diff;
best = cur;
} else if (diff == bestDiff) {
if (betterTie(cur, best)) {
best = cur;
}
}
}
cur[10] -= arrowsLeft;
return;
}
int score = 10 - idx;
int need = info[idx] + 1;
if (need <= arrowsLeft) {
cur[idx] = need;
dfs(idx + 1, arrowsLeft - need, lion + score, apeach, info, cur, best, bestDiff);
cur[idx] = 0;
}
if (info[idx] > 0) {
dfs(idx + 1, arrowsLeft, lion, apeach + score, info, cur, best, bestDiff);
} else {
dfs(idx + 1, arrowsLeft, lion, apeach, info, cur, best, bestDiff);
}
}
vector<int> solution(int n, vector<int> info) {
vector<int> best(11, 0);
vector<int> cur(11, 0);
int bestDiff = -1;
dfs(0, n, 0, 0, info, cur, best, bestDiff);
if (bestDiff <= 0) {
return vector<int>{-1};
}
return best;
}
🔗 문제 링크
양궁 대회
https://school.programmers.co.kr/learn/courses/30/lessons/92342
✔️ 소요된 시간
40분
✨ 수도 코드
접근법
일단 제일 처음 생각한 것은 "브루트포스로 풀 수 있을까?" 였습니다.
이 문제의 최대 경우의 수를 생각해봅시다.
음이 아닌 정수
이런 문제는 중복조합으로 풀 수 있습니다. (고등학교 때 배운 기억이 나실 것입니다.)
경우의 수를 구하면
11H10 = 20C10 = 184,756
이 됩니다. 이 숫자면 충분히 브루트포스로 풀 수 있습니다.
수도코드
풀이 흐름은 다음과 같습니다.
info배열과 비교해서 둘의 점수 차이를 계산하여 최댓값을 갱신한다.-1을 return 한다.풀이1: 최초 작성 코드
어피치와 라이언의 점수를 계산하는 함수
백트래킹으로 모든 중복조합을 담은 배열을 반환하는 함수
r == 1즉, 마지막 칸에는 남은n을 전부 넣어서 끝냅니다.n개까지 넣는 경우를 모두 탐색하고, 남은 걸 재귀로 넘긴다.results에 누적해서 반환합니다.Solution
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4],[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3], ... 순서대로 정렬됩니다.풀이2:
combinations_with_replacement사용파이썬은 중복조합을 뽑아주는 라이브러리가 있으므로 좀 더 편하게 작성할 수 있습니다.
코드는 '바킹독' 님의 코드 링크로 대체합니다.
위 코드에서는 저 처럼 라이언 점수, 어피치 점수를 따로 구해서 차이를 계산하지 않고 한 번에
score변수에 라이언과 어피치 점수의 차이를 저장했다는 점이 다릅니다.그리고 모든 조합을 뽑아서 정렬을 하지않고
cmp라는 정렬 함수를 작성해서 매번 해당 조합이 더 앞에 있는 순서인지를 비교하고 있습니다.ret[11]에는 점수 차이를 넣고, 전체 배열을 정렬하는 방법으로 자연스럽게 점수 차이가 가장 큰 경우가 맨 앞에 오도록 작성했습니다.풀이3: 그리디
출처: https://blog.encrypted.gg/1028
사실 브루트포스로 충분히 풀려서 고려도 안했지만, 이 문제를 그리디로도 풀 수 있습니다.
라이언이 가장 큰 점수 차이로 우승할 수 있는 방법이 여러 가지 일 경우, 가장 낮은 점수를 더 많이 맞힌 경우를 return 하라는 조건이 큰 힌트입니다.
라이언이 이길 점수 구간을 고르면 그 과녁에서는 어피치보다 1발만 더 쏘면 되고, 남는 화살은 0점에 몰아 넣으면 위 조건을 만족하면서 가장 큰 점수차이로 이길 수 있게됩니다.
1에서 10점 각각에 대해 라이언이 해당 과녁 점수에서 이길지 말지를 정하는 총$2^{10}$ 개의 가능성에 대해서만 확인하면 됩니다.
코드 : https://github.com/encrypted-def/kakao-blind-recruitment/blob/master/2022-blind/Q4_2.py
📚 새롭게 알게된 내용