01/22 본캠프#20

guno park·2024년 1월 22일
0

본캠프

목록 보기
20/77

알고리즘 풀어보기

햄버거 만들기

내가 생각한 풀이 (첫번째)

인덱스 순서가 1231이니까 문자열에서 1231이 나올때마다 제거해서,횟수만큼 +해주는 방식

using System;
using System.Linq;

public class Solution {
    public int solution(int[] ingredient) {
        //빵-야채-고기-빵 (1-2-3-1순서로만 포장함)
        //하나 만들면 그 인덱스 제외하고 다시
        //조건을 어떻게 걸까, 하나 만들고 빼고 반복해야됨.
     var mayList = ingredient.ToList();
         int answer = 0;
        string a = string.Join("",ingredient);
        while (a.IndexOf("1231") != -1)
        {
            answer++;
            a = a.Substring(0,a.IndexOf("1231"))+a.Substring(a.IndexOf("1231")+4);
        }
        return answer;
    }
}

문제 발생

시간초과 > 시간이 초과되면 리소스 많이 먹는 부분을 엎어야함.
여기서는 IndexOf와 substing이 많이 먹는다고 판단.

두번째 풀이

1231이라는 인덱스를 다 제거하고, 없어질때까지 반복 후, 원래 문자열과 길이를 비교해서 차이/4한 만큼 반환했음.

using System;
using System.Linq;

public class Solution {
    public int solution(int[] ingredient) {
        //빵-야채-고기-빵 (1-2-3-1순서로만 포장함)
        //하나 만들면 그 인덱스 제외하고 다시
        //조건을 어떻게 걸까, 하나 만들고 빼고 반복해야됨.     
        int answer = 0;
        string a = string.Join("",ingredient);
        while (a.IndexOf("1231")!=-1)
        {            
           a=string.Join("",a.Split("1231"));           
        }
        return answer = (ingredient.Length-a.Length)/4;
    }
}

문제 발생

일반적인 경우에는 해결되지만, [1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1] 이런 식으로 나올 경우
[1, 2, 3, 2, 3, 1] 이렇게 계산됨. 원래는
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1] 1번
[2, 3, 1, 2, 3, 1] 2번
[2, 3] 3번
이렇게 3회가 나와야 함.

참고해서 풀이

참고 링크

using System;
using System.Collections.Generic;

public class Solution {
    public int solution(int[] ingredient) {
        int answer = 0;
        var list = new List<int>();

        foreach (int num in ingredient)
        {
            // 리스트에 하나씩 추가해나감
            list.Add(num);

            // list의 크기가 4이상이면 체크 시작
            if (list.Count >= 4)
            {
                // 햄버거를 만들 수 있다면 무조건 마지막 인덱스를 기준으로 이루어짐
                if (list[list.Count - 4] == 1 && list[list.Count - 3] == 2 &&
                    list[list.Count - 2] == 3 && list[list.Count - 1] == 1)
                {
                    // 햄버거를 만듬
                    answer++;
                    list.RemoveRange(list.Count - 4, 4);
                }
            }
        }
        return answer;
    }
}

차이점 비교

문자열로 비교할 경우 하나씩 제거가 C#에서는 리소스를 많이 사용하기 때문에 해결하지 못하고, 하나씩 비교하는 방법은 생각이 안남.
참고자료의 경우 1231이라는 순서를 리스트로 만들어 4이상일때 마지막으로 들어온 숫자에서 앞의 3개까지 바로바로 비교해 인덱스로 제거하는 방식을 사용함.

느낀 점

머리가 말랑말랑한 사람들이 부럽다... 자꾸 혼자 힘으로 못 풀고 참고하다보니 자신감이 떨어지는 느낌이다.
아직까지도 무작정 Index를 찾거나, 전체를 나누려하는 습관이 있다. C#답게 하나씩 때려박아보자.

성격 유형 검사하기

문제 링크

생각한 풀이

유형별 점수를 딕셔너리로 만들고, 설문결과를 스위치문으로 작성하여 해당하는 키값의 밸류에 점수를 더하는 방식으로 작성했다.

 Dictionary<char, int> MBTI = new Dictionary<char, int>()
        {
            {'R',0}, {'T',0},
            {'C',0}, {'F',0},
            {'J',0}, {'M',0},
            {'A',0}, {'N',0},
        };
        for (int i =0;i<survey.Length;i++){
            switch (choices[i])
            {
                
                case 1:
                    MBTI[survey[i][0]] +=3;
                    break;
                case 2:
                    MBTI[survey[i][0]] +=2;
                    break;
                case 3:
                    MBTI[survey[i][0]] +=1;
                    break;                
                case 5:
                    MBTI[survey[i][1]] +=1;
                    break;
                case 6:
                    MBTI[survey[i][1]] +=2;
                    break;
                case 7:
                    MBTI[survey[i][1]] +=3;
                    break;
            }
        }

설문결과 정산 후에 answer 배열의 앞부터 if문으로 대소비교를 해서 차례대로 넣어 완성해준다.

    #region 맨앞
        if (MBTI['R'] < MBTI['T'])
            answer[0] = "T";
        else if (MBTI['R'] > MBTI['T'])
            answer[0] = "R";
        else 
            answer[0] = "R";
        #endregion
        
        #region 2
        if (MBTI['C'] < MBTI['F'])
            answer[1] = "F";
        else if (MBTI['C'] > MBTI['F'])
            answer[1] = "C";
        else 
            answer[1] = "C";
        #endregion
        
        #region 3
      
        if (MBTI['J'] < MBTI['M'])
            answer[2] = "M";
        else if (MBTI['J'] > MBTI['M'])
            answer[2] = "J";
        else 
            answer[2] = "J";
        #endregion
        
        #region 맨뒤
        if (MBTI['A'] < MBTI['N'])
            answer[3] = "N";
        else if (MBTI['A'] > MBTI['N'])
            answer[3] = "A";
        else 
            answer[3] = "A";        
        #endregion

정리

평소에 딕셔너리를 잘 사용하지 않았는데, 어느 값에 종속되는 값이 있을 때 사용하니까 정말 편하다.

바탕화면 정리하기

문제 링크

풀이

시작지점 s와 끝지점 E를 각각 반복문을 사용해 찾아주도록 했다.
s의 경우 가장 작은 값을, e는 가장 큰 값을 가져오도록 작성해주었고
제한범위 밖의 값으로 초기화를 해주었을 때 잔고장이 없었다.

using System;

public class Solution {
    public int[] solution(string[] wallpaper) {
        int[] answer = new int[4];
        int sx =100;
        int sy =100;
        int ex =-100;
        int ey =-100;
        for (int i =0;i<wallpaper.Length;i++){
            int index = wallpaper[i].IndexOf('#');
            if (index!=-1){
                if(sx>=i)
                    sx=i;
                if(sy>=index)
                    sy=index;
            }
        }
        for (int i =wallpaper.Length-1;0<=i;i--){
            int index = wallpaper[i].LastIndexOf('#');
            if (index!=-1){
                if(ex<=i)
                    ex=i+1;
                if(ey<=index)
                    ey=index+1;
            }
        }
        answer[0]=sx;
        answer[1]=sy;
        answer[2]=ex;
        answer[3]=ey;
        return answer;
    }
}

정리

내가 원하는 값을 얻기 위해서 조건을 고심해서 작성해주어야 한다.
그리고 s의 경우 왼쪽, 앞에서부터 찾고 e의 경우 뒤에서부터 찾기위해 LastIndexOf를 사용했다.

개인정보 수집 유효기간

문제 링크

풀이

조금 길어서 정리가 안되지만 순서대로 정리해보자면,

  1. 오늘 날짜를 연도, 달, 일 3개로 각각 분리한다.
  2. for문을 구성한다. 선택된 정보의 날짜와 약관을 따로 분리하고, 약관에 따라 수집 유효기간으로 바꿔준다.
    2-1. 약관의 기간은 달 단위로 책정되어 있다. 만약 현재 달+약관 기간을 했을 때 12달(1년)을 넘어가면 12달 이하가 될 때까지 -12를 하면서 연도를 ++해준다.
  3. 새로 만들어진 수집 유효기간을 바탕으로 조건을 달아 파기된 개인정보의 index를 answer에 반환해준다.
using System;
using System.Collections.Generic;

public class Solution {
    public List<int> solution(string today, string[] terms, string[] privacies) {
        List<int> answer = new List<int>();
        int TodayYear = int.Parse(today.Split('.')[0]);
        int TodayMonth = int.Parse(today.Split('.')[1]);
        int Todayday = int.Parse(today.Split('.')[2]);
        for (int i = 0; i < privacies.Length; i++)
        {
            string[] splitdate = privacies[i].Split(new Char[] { '.', ' ' });
            int year = int.Parse(splitdate[0]);
            int month = int.Parse(splitdate[1]);
            int day = int.Parse(splitdate[2]);
            char term = char.Parse(splitdate[3]);
            int MonthPlus = 0;

            foreach (string a in terms)
            {
                if (a[0] == term)
                {
                    string[] b = a.Split(' ');
                    MonthPlus = int.Parse(b[1]);
                    break;
                }
            }

            month += MonthPlus;
            if (month > 12)
            {
                while (month > 12)
                {
                    month = month - 12;
                    year++;
                }
            }
            
            if (year < TodayYear)
            {
                answer.Add(i + 1);
            }
            else if (year == TodayYear)
            {
                if (TodayMonth > month)
                     answer.Add(i + 1);
                else if (TodayMonth ==month)
                {
                    if (day <= Todayday)
                    {
                        answer.Add(i + 1);
                    }
                }
                }
            }
            return answer;
    }
}

트러블슈팅

조건에 따른 예외처리
계속 생각하니 머리가 어지러워져서 좀 오래 걸렸는데,
처음에는 단순하게 작성했는데 월 일 부분에서 문제가 발생했다.

    if (year < TodayYear)
    {
    	answer.Add(i + 1);
	}
    else
    {
    	if (month < TodayMonth)
        {
        	answer.Add(i + 1);
		}
        else 
        {
        	if (day <= Todayday)
            {
            	answer.Add(i + 1);
             }
         }
      }

디버깅을 살살 돌려보니 뭔가 이상한데 계속 인지부조화가 와서 시간이 걸렸다.
연 월 일 순으로 유효기간의 날짜가 오늘을 지났으면 바로 파기된 것으로 입력하고, 같으면 큰것에서 작은 것 순으로 차차 비교하는 방식으로 조건을 다시 작성했다.

문자열 분리하기
Split 구분자 여러개 사용하는 방법
주어진 개인정보에 날짜와 약관 유형이 적혀있는데, Split('.')으로는 날짜와 유형이 같은 배열에 들어가서 구분자를 여러 개 작성하는 방법을 찾아봤더니 있었다.

달리기경주

내가 생각한 풀이

간단하게 호출된 선수의 인덱스를 찾아 앞뒤를 swap하는 방식으로 작성하였다.
정말 간단해서 날먹하려나 싶었는데 시간이 계속 초과되었다.

using System;

public class Solution {
    public string[] solution(string[] players, string[] callings) {
        string[] answer = new string[] {};        
        for (int i =0;i<callings.Length;i++){
            string name = callings[i];
            for (int j=0;j<players.Length;j++){
                if (players[j]==name){                
                string temp =players[j-1];
                players[j-1] = name;
                players[j]=temp;
                break;
            }                
            }
        }
        return answer=players;
    }
}

트러블 슈팅

참고 자료
참고자료를 보고 다시 만들었다.

using System;
using System.Collections.Generic;

public class Solution {
    public string[] solution(string[] players, string[] callings) {
        string[] answer = new string[] {};
        Dictionary<string, int> playerDic = new Dictionary<string, int>();
        int a = 1;
        foreach (var VARIABLE in players)
        {
            playerDic.Add(VARIABLE,a++);
        }

        for (int i = 0; i < callings.Length; i++)
        {
            string callname = callings[i];
            int num = playerDic[callname]-1;
            string changename = players[num - 1];
             playerDic[callname]--;
            playerDic[changename]++;
            players[num - 1] = callname;
            players[num] = changename;
        }
        return answer=players;
    }
}

여기서도 딕셔너리를 사용해 해당 선수의 이름과 순위를 저장하고, 반복문을 통해 호출 되었을 때 딕셔너리의 밸류값을 바꿔줌과 동시에 players 배열의 순서도 바꿔준다.
딕셔너리가 자주 나오는데, 사용해야할 부분이 좀 명확한 것 같다.

공원산책

문제 링크

풀이

  1. 현재 위치를 특정한다. 이중반복을 통해 현재 좌표를 int배열에 저장해주었다.
 int[] Start = new int[2];
 for (int i = 0; i < park.Length; i++)
 {
 	for (int j = 0; j < park[i].Length; j++)
    {
    	if (park[i][j] == 'S')
        {
        	Start[0] = i;
            Start[1] = j;
            break;
         }
      }
   }
  1. routes를 반복을 통해 하나씩 불러와 방향과 이동 거리를 분리하여 스위치를 사용해 분기 시켜준다.
    2-1. 먼저 이동한 위치가 공원 밖인지 확인한 다음, 장애물을 검사한다. 검사를 같이 하거나 순서를 반대로 하면 OutOfRange 에러가 발생한다.
    2-2. 한칸씩 이동하다 막히면 다음 명령으로 넘어간다. 이동할때마다 위치를 갱신해준다.
 for (int i = 0; i < routes.Length; i++)
        {
            char a = routes[i][2];
            int b = (int)a - 48;
            switch (routes[i][0])
            {
                case 'E':
                    for (int j = 0; j < b; j++)
                    {
                        if (Start[1] + 1 < park[i].Length)
                            if (park[Start[0]][Start[1] + 1] != 'X')
                                Start[1] += 1;
                            else break;
                        else break;
                    }
			}
		}
  1. 이동이 끝나면 좌표를 answer로 반환한다.

트러블슈팅

이동할 수 없을 때
한칸씩 이동하는 게 아니라, 한번에 이동을 하는데 만약 할 수 없으면 다음 명령으로 넘어가는 거였다. 이렇게 될 경우, 만약 이동범위가 공원 밖이라면 바로 다음 명령으로 넘어가면 되지만, 공원 내라면 한칸씩 장애물 유무를 검사해주어야한다. 그래서 식을 살짝 바꿔주었다.

if (Start[1] + b < park[0].Length)
{
	for (int j = 1; j <= b; j++)
	{
    	if (park[Start[0]][Start[1] + j] != 'X') 
			check++;
	}
	if (check == b)
    Start[1] += b;
}

어차피 공원인지 아닌지 검사는 한방에 해도되니까 반복문 밖으로 빼주고, 한칸씩 검사해야하는 장애물만 반복하는데, 넘어갈 수 있을때마다 check++를 해주고, 같을때만 이동시켜주었다.

OutOfRange 문제
이 문제에서 공원의 모양이 직사각형이라 좌우 이동을 할 때 아무 배열이나 잡아도 되겠다 싶어서 park[i].Length 를 넣어줬는데, 이 i가 routes.Length만큼 증가하기 때문에 크기가 더 커져서 발생한 문제였다. 그래서 그냥 park[0].Length 로 고정해줬다.

객체지향 특강 기본 정리

작업 시 파일을 분리해야 하는 이유

코드가 커질수록 보기가 힘들다.
->코드를 파악하기 힘들고 버그가 더 쉽게 일어날 수도 있고 찾기가 힘들다
->코드의 유지보수가 힘들다

길어질수있는 코드를 나눠서 관리 -> 코드 분석 쉬움

목적에 맞게 코드를 분리해서 관리, 연관된 데이터 묶어서 사용
->코드파악이 쉽다, 가독성이 증가한다, 유지보수가 쉽다, 재활용성이 증가한다.
=코드를 이해하기 쉽다

단일책임의 원칙

기준을 어떻게 잡아야 하는가

예시 장착이라는 기능은 Player , item , inventory 셋 중 누가 가져야될까
item은 상점에 있을수도 있고, 몬스터가 가지고있을수도 있어서 합당하지 않음

※이유만 합당하다면 cs파일이 많아져도 상관없음.

깃허브에 유니티 커밋하기 (중요)

커밋할 파일들

  1. 에셋폴더
  2. 프로젝트 세팅 폴더
  3. 패키지 폴더

주의사항

  1. 깃이그노어랑 폴더세개랑 같은 선상에 있어야 됨

  2. Read.me에는 중요한 내용만 언급하면 된다. 이 코드는 어떻게 작성됬고 이러쿵 저렁쿵 길게 쓸 필요 없다.

레포지토리 생성 방법

예전에 특강에서 나온 내용,

    1. 커밋할 폴더의 상단 경로까지 지정
    1. 넣을 폴더 이름을 네임으로 지정
    1. 레포지토리 생성 시 gitignore를 유니티로 설정해준다.

레포지토리 이름을 폴더 이름으로 하고 생성해야 된다.
그렇게 해야 그 폴더 안에 깃을 생성할 수 있다..
폴더 이름에 공백 있으면 안됨.

개인과제 수정

문득 개인과제 해설 영상을 보다가, 마우스 포인트에 따라 방향 전환을 해야된다는 사실을 깨달아서 원래 있던 키보드 입력에 따른 방향전환을 삭제하고 바꿨다.
바꾸는 중에 생겼던 트러블 슈팅에 대해 다뤄보자면,
1. 마우스 포지션 입력을 받았을 때, 각도로 변환한 값을 살펴보니 전환 기준점이 30도까지밖에 안나왔다.
2. 마우스 포지션을 다시 받아봤는데 값이 엄청 작게 나왔다.
3. 강의때 사용하던 방식을 차용해 들고왔는데 강의때 만든 게임이랑 값이 너무 차이나서 뜯어본 결과,

Vector2 LookInput = value.Get<Vector2>().normalized;

이렇게 해버렸다. 벡터를 정규화시켜 버려서 엄청 작게 나온 것이였다.
노멀라이즈드를 제거하고 실행하니 정상적으로 잘 동작하였다.

내일 할 일

  1. 알고리즘 풀기
  2. 개인과제 해설영상 보고 정리하기
  3. 개인과제 보고 수정할 사항 있으면 수정하기

0개의 댓글