01/12 본캠프 #14

guno park·2024년 1월 12일
0

본캠프

목록 보기
15/77

알고리즘 풀어보기

자연수 뒤집어 배열로 만들기

생각한 풀이

어제 풀어본 자릿수 더하기를 응용해 배열에 저장하는 방법을 선택했다.

public static long[] solution(long n)
{
    int a = (int)MathF.Log10(n);
    long[] answer = new long[a+1];
    int d = a;
    for (int i = a; 0 <= i; i--)
    {
        double b = n / MathF.Pow(10, i);
        long c = (long)b;
        answer[d--] = c;
        n = n - c * (long)Math.Pow(10, i);
    }
    return answer; 이거 마지막 13번째꺼 빼고 다 돌아가는데 왜 걸리는지 모르겠음.
}

발상은 괜찮았으나, 코드 제출하니까 13개의 테스트 중에 하나가 계속 실패했다.
여기서는 이유를 띄워주지 않아서 실패 이유를 유추해보자면
1. double b를 생성 할 때 특정한 숫자들이 값이 변한다.
2. 주어진 숫자 n이 중간에 0을 포함하고 있다면 0이 맨 앞에 올 때가 있을 것이다.
정수 자료형에서는 0이 제일 앞에 오는 것을 허락하지 않기 때문에 0이 사라질 것이다.

ex) 1023이라고 했을 때, 제일 앞 자릿수를 떼면 023이 된다. 정수 자료형에서는 023은 존재할 수 없으니 23이 된다.

이 두가지 이유가 제일 합당하다고 생각했다.

다른 문제 풀이

다르게 접근해 본 방식은 n을 문자열로 전환 한 후, 다시 문자로 분리하는 것이다.
발상은 했으나, 방식을 몰라 인터넷을 찾아봤더니

.ToCharArray()

라는 기능이 있었다. (출처 : 마소)

위 발상을 바탕으로 순서를 설명하자면,
1. 다른 배열에 n과 똑같은 순서로 먼저 변환을 해준다.
2. 스택에 저장한다.
3. 반환할 answer에 int로 Parse하면서 Pop을 해준다.

스택을 반복 사용할 때는, 특정 갯수만큼 한다면 for문으로 작성하는 것도 좋겠지만,
만약 전체를 꺼낸다거나 하고싶을 때는 while(!(stack.Count == 0)) 이렇게 작성하는 것도 괜찮은 방법일 듯 하다.
stack.Count는 현재 스택의 길이를 반환하기 때문에 Pop을 해줌에 따라 값이 변하기 때문에 고정상수로 사용할 수 없다. 굳이 사용한다면 다른 변수에 초기 Count를 저장하고 사용하는 정도?

public static List<int> solution(long n) 
{   
    List<int> answer = new List<int>();
    string a = n.ToString();
    char[] charArray = a.ToCharArray();
    Stack<string> stack = new Stack<string>();
    foreach (char c in charArray)
        stack.Push(c.ToString()) ;
    while(!(stack.Count == 0)) 
    {
        answer.Add((int.Parse(stack.Pop())));
    }
    return answer;
} //이걸로 성공

쉽고 알아두면 많이 쓸 방법

이건 처음에 생각한 방식이 왜 안되는지에 대한 의문이 들어서 튜터님께 여쭤보고 얻은 내용이다.

  • 10으로 나머지를 구하면 1의자리를 제외한 값 (%10)
  • 10으로 나누면 1의자리 (/10)
for (int i = 0; x > 0; i++)
{
   sum += x % 10;
   x /= 10;
}

이런 방식으로 구하면 n이 몇자리 수인지 구할 필요도 없고,
간단하게 자릿수 값을 얻을 수 있다.

프로그래밍 하면서 나머지랑 나누기를 활용하는 경우가 많습니다.
굉장히 자주나오는 문제고 활용할데도 많으니 익혀두시면 좋습니다. -김영호 튜터님

정리

사람 생각하기 나름이라고 어렵게 생각하고 나면 계속 그 방법밖에 안 떠오르더라.
최대한 간단하게 구현할 수 있는 방법을 생각해보자.
물론 그 복잡한 정도를 알려면 많이 알아야된다.

정수 제곱근 판별

최초 풀이

  1. 주어진 양의 정수 n을 MathF.Sqrt로 제곱근을 구한다. (여기선 double 일거임)
  2. 이 제곱근을 정수형으로 변환한다. (만약 소숫점이 있다면 이 과정에서 제거된다)
  3. 제곱근을 MathF.Pow로 다시 제곱으로 만들었을 때, n과 같다면 +1해서 제곱한 값을 반환하고, 다르다면 -1을 반환한다.

굳이 설명하고 있는 이유는? 이게 안되더라.
생각에는 아마도 형변환 거치면서 데이터가 손상됬을 것 같다.. (MathF.Sqrt 기능)

두번째 풀이

다음으로는 단순 무식하게 작성해봤다.

for (int i =0; i*i<=n;i++)
{
if (i*i == n)
answer = (i+1)*(i+1);
else
answer = -1;
}
return answer;

이 풀이의 문제점 : n이 크면 시간이 오래 걸린다. Big O로는 아마 O(n)일텐데, 범위가 커서 그런가보다. 그래서 10초 초과로 통과 못 했다.

세번째 풀이

using System;
public class Solution {
    public long solution(long n) {
        long answer = 0;
        long a = (long)MathF.Sqrt(n);
        if (a*a==n)
            answer = (a+1)*(a+1);
        else
            answer = -1;
        
        return answer;
    }
}

이번엔 더 단순하게 접근해봤다. 어차피 두개 중 하나라서
1. n의 제곱근을 구한다.
(MathF.Sqrt가 double형이여도, 제곱근이 있는 숫자들은 정수값이 온전하게 반환되더라.)
2. if로 a*a가 n이면 a+1의 제곱을 반환 / 아니라면 -1 반환
(Pow를 안 쓴 이유는 또 오류날까봐)

이렇게 했더니 통과가 됬다.

정리

이게 아마 두번째로 푼 문제인가 그런데, 알고리즘 풀면 풀수록 형변환 막 하는게 아니라는 걸 느낀다. 될 수 있으면 최대한 원본 데이터 값을 보존하면서 쓸 수 있는 알고리즘을 만들어야겠다.

정수 내림차순으로 배치하기


이건 원래 푼 방법이 있고, 지금 작성하면서 떠오르는 방법이 있어서 둘 다 포스팅 해보도록 하겠다.

원래 방법

  1. MathF.Log10을 이용해 n의 자릿수를 구한다.
  2. 자릿수 값들을 따로 저장할 배열을 만들어준다.
    (자릿수보다 +1 : 자릿수이기 때문에 실제 수보다 1 적음)
  3. n을 문자열로 변환한 다음, 문자 단위로 쪼갠다.
  4. 만든 어레이에 쪼개진 문자들을 옮겨 담는다. (char기 때문에 int.Parse)
  5. OrderBtDescending을 통해 오름차순으로 정렬한다.
  6. 아까 저장한 n의 자릿수를 이용해서 하나씩 꺼내오면서 크기에 맞게 더해준다.
  7. 완성!
using System;
using System.Linq;
public class Solution {
    public long solution(long n) {
        long answer = 0;
        int leng = (int)MathF.Log10(n);
        int[] arr = new int[leng+1];
        string num = n.ToString();
        char[] charArray = num.ToCharArray();
        for (int i = 0; i<leng+1;i++){
            arr[i] = int.Parse(charArray[i].ToString());
        }
        arr= arr.OrderByDescending(x => x).ToArray();
        int j=0;
        for (int i = leng; 0<=i;i--){
            answer+= arr[j++]*(long)MathF.Pow(10,i);
        }
        return answer;
    }
}

이렇게 하는 것도 괜찮은데, 튜터님이 가르쳐준 방법도 쓸 수 있을 듯 하다.

미세 팁

 int j=0;
 for (int i = leng; 0<=i;i--){
 	answer+= arr[j++]*(long)MathF.Pow(10,i);
 }

이렇게 작성하면 증감하는 변수 두개 동시에 쓸 수 있음.

다른 방법

  1. 몇칸이 될지 모르니까 List로 저장할 공간을 할당한다.
    (이번에도 범위가 넓기때문에 long으로)
  2. list에 %10 한 값들을 차례차례 담아준다. n/=10 으로 계속 n을 줄여나가면서 한다.
  3. 위처럼 오름차순 정렬을 한 후, 자릿수에 맞게 리스트 값을 더해준다.
using System;
using System.Linq;
using System.Collections.Generic;
public class Solution {
    public long solution(long n) {
        long answer = 0;        
        List<long> list = new List<long>();
        for (int i = 0; n!=0;i++){
            list.Add(n%10);
            n/=10;            
        }
        list = list.OrderByDescending(x => x).ToList();        
        for (int i = 0; i <list.Count;i++){
            answer += list[i]*(int)MathF.Pow(10,list.Count-i-1);
        }
        return answer;
    }
}

서울에서 김서방 찾기

풀이

(출처 : DevStory)
쉽고 원래 아는 메서드긴 한데, string을 찾아본 적은 없어서 작성함.

_playeritems.IndexOf(i) == -1

그 전에는 배열.IndexOf(Item) 이렇게 해서 아이템이 인벤토리에 존재하는 지 찾아낼 때 사용했었다.

using System;

public class Solution {
    public string solution(string[] seoul) {
        string answer = "";
        int a = Array.IndexOf(seoul,"Kim");       
        return answer = $"김서방은 {a}에 있다";
    }
}

아마 이게 정식 방법이지 않을까싶다.

핸드폰 번호 가리기

풀이

  1. 받아온 문자열의 길이를 저장한다.
  2. 뒤 4자리만 사용할 것이기 때문에, 나머지는 다 지워준다.
  3. 앞에서부터 차례대로 *로 채워준다.

이런 느낌으로 작성하긴 했는데, 아마 *로 채우는 과정을 저렇게 안써도 될 듯 하긴하다.
(출처 : Coding_abc.kr)

public class Solution {
    public string solution(string phone_number) {
        string answer = "";
        int length = phone_number.Length;
        answer = phone_number.Remove(0,length-4);
        for(int i=0;i<length-4;i++){
            answer = answer.Insert(i,"*");
            }            
        return answer;
    }
}

이 방법도 처음 써봐서 포스팅 함.

팀 프로젝트 - TextRpg 만들기

enum 캐스팅

enum을 캐스팅하는 함수를 만들지 않아도 Enum형의 경우에는 그냥 int 캐스팅을 해도 된다.
값을 지정하지 않는 경우 첫번째 요소부터 0,1,2... 값이 자동으로 지정되어 있다.

ex 1)
enum JOB { Intern, Assistant, Junior, Senior };
// 각각 0, 1, 2, 3 값이 들어가있음
int a = (int)JOB.Intern; // a에 0이 들어감

ex 2)
enum JOB { Intern =4, Assistant, Junior, Senior };
이렇게 한 경우에는 4,5,6,7
지정하지 않은 숫자의 경우 앞에 지정한 숫자 + 1

ex 3)
enum JOB { Intern =4, Assistant, Junior=3, Senior };
이 경우에는 Senoir에도 4값이 들어갑니다

보스 아이템 관련

오늘 아이템 드랍과 관련되어 몇가지 얘기를 했었다.

1. 어느 상황에서 드랍될 것인가

우리 팀 프로젝트 컨셉 상, 일반 몬스터를 잡고 드랍되는 것 중에 아이템이 드랍되는 것은 맞지 않다고 판단하여, 던전 클리어 보상으로 제공하기로 하였다.

2. 무엇을 드랍할 것인가.

현재 장착 가능한 아이템을 종류 별로 한 개씩, 그리고 같은 물건을 더 얻을 수 없게 일정 클리어 이상부터는 던전 클리어 트로피가 주어진다.

3. 어떤 방식으로 줄 것인가.

던전 자료 저장용 클래스에 아이템을 생성하고, 던전을 클리어 할 시 인벤토리에 지급되는 방식으로 작성하였다.

몬스터 관련

보스 추가

회의 결과 일반 보스와 엔딩용 보스를 구분하기로 하였고, 그에 따라 일반 보스보다 어려운 보스를 하나 만들었다.

그에 따라 엔딩용 보스가 등장할 조건을 걸어주기 위해, 직업 enum의 값을 조건으로 작성했다.

몬스터 공격력 재설정

기존 몬스터의 공격력은 방어력을 제외한 채로 작성 했으나, 방어력 수치가 유명무실 해지기에 방어력을 고려한 수치로 재설정 하였다.

아이템 관련

소비형 아이템 사용 시, info창에 아이템 별 수치가 작성되지 않았었는데,
Console 출력하기 전에 string 변수를 하나 만들어 아이템 이름에 따라 string을 다르게 해서 출력해주었다. 왜냐면 아이템 클래스에 HP나 MP 회복량에 관한 변수가 없다.

그래서 팀원분이 그냥 HP : 공격력, MP : 방어력 이렇게 쓰자고 하셨는데, 다음 주에 병합하고 후처리 할 때 작업해야겠다.

정리

오늘은 딱히 오류도 없고 한 것도 없고 그렇다. 오탈자 수정이나 컨셉 검수, 주석 작성 이런 것만 한 듯 하다.

코딩 팁

전처리문

#define TEST
`
`
`
`
#if TEST
            Test(100000);
#endif

맨 위에 TEST 정의 되있으면 동작하고 없으면 안함.

주석 처리에 관한 건

주석 처리가 엄청 예쁘게 된 예시글을 주셔서 나중에도 볼 수 있게 포스팅한다.
(github)

///

(출처 : 에반, 어른반)

주석에 관해 정리된 글인데, ///은 처음 보는 주석이라 작성한다
///은 자동완성 주석으로 함수 위에 사용한다.
사용 시 이 함수의 개요에 대해 적을 수 있는 칸과, 매개변수들이 무엇을 의미하는 지 작성 할 수 있는 공간을 제공한다.
매개 변수가 없을 시 개요 작성만 제공한다.

#region

(출처 : All about it.)
작업 시 가독성을 높여줄 기능이 될 것 같다. VS에서도 기본적으로 토글을 제공하긴 하지만,
접을 수 있는 패턴이 한정적이라 잘 활용해서 관련있는 것들끼리 묶어서 관리하면 좋을 듯?

오늘 총 정리

막 엄청나게 어렵게 한 건 없고, 짜잘짜잘하게 이것저것 했다. 뭐라 정리 할 것도 딱히 없는 하루다.

내일 할 일

내일은 주말이니까 밀린 문법 공부들을 할 예정이다.

0개의 댓글