숫자의 조합을 만들어서 소수인지 판별하면 되는 문제이다.

 

조합은 3중 for문을 사용해서 구현하였으며 소수 판별은 시간복잡도를 줄이기 위해 sqrt(num) 값까지만 계산하게 해두었다.

#include <vector>
#include <iostream>
#include <cmath>
using namespace std;

bool isPrime(int num){
    if(num<=2) return false;
    for(int i=2;i<=sqrt(num);i++){          //시간복잡도 줄이기
        if(num%i==0) return false;
    }
    return true;
}

int solution(vector<int> nums) {
    int answer = 0;

    for(int i=0;i<nums.size();i++){
        for(int j=i+1;j<nums.size();j++){
            for(int h=j+1;h<nums.size();h++){
                int n= nums[i]+nums[j]+nums[h];     //조합고려
                if(isPrime(n)){
                    answer++;
                }
            }
        }
    }
    return answer;
}

언리얼 공부를 하면서 무작정 유투브강의를 찾아 보며 어떤걸 따라 만들어 볼 지 고민했는데 내가 만들고 싶은 게임인 RPG게임을 따라 만드는게 도움이 많이 될 것 같아서 유투브에 있는 RPG 게임 Tutorial를 보면서 따라 만들어 보기로 했다. 영어로만 된 강의라 이해하기 어려웠지만 천천히 듣고 보면서 배워 나갔다. 

https://www.youtube.com/watch?v=WcDj4uZygyE&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=2

 

나는 이 강의를 보고 시작했다. 

일단 기본적인 걷고 뛰는 것을 만들어 보았는 데 이때 애니메이션이 값에 따라 자연스럽게 이어지도록 해주는 것이 Blend Space 1D 이다. 

1. FBX import

일단 서있고 걷고 달리는 애니메이션 을 넣어줘야한다.

이때 애니메이션에 사용될 FBX파일 언리얼 프로젝트에 넣어주면되는데 혹시 모를 경우를 대비해 디폴트로 리셋하고

메시를 자신이 사용할 스켈레톤을 넣어줘야한다. 여기서 나는 스켈레톤인 SK_Mannequin으로 플레이를 만들것이라 SK_Mannequin으로 선택하고 임폴트 해주었다. 

2. Blend Space 1D

Blend Space 1D는 변수의 값에 따라 애니메이션이 자연스럽게 이어지도록 하는 애니메이션 흐름도라고 보면 될 것 같다.

애니메이션 변환을 위한 변수선

이때, 가로축의 이름을 본 사진과 같이 Speed로 해주면되는데 이것은 애니메이션이 Speed의 값에 따라 바뀌게 하기위한 변수선언이라고 보면 되며 이때 750정도를 max값으로 해서 달리기값의 최대를 정해주면 된다. 

애니메이션 삽입

그리고 위의 화면에 보이는 곳에 왼쪽이 Speed값이 0 오른쪽이 최대값인 750 이여서 이에 맞는 애니메이션을 넣어주면 된다. 

3.Animation Blueprint 

그 다음 Animation Blueprint를 만들어 주면 된다. 밑의 사진은 다 만들어진 상태이다. 

중요한 것은 state machine이다. state machine은 Cache pose를 사용하기 위해 애니메이션을 하나의 객체처럼 만드는 것이다. transition은 어떨때 애니메이션이 다른 애니메이션으로 변환되는 지 설정하는 것으로 지금에서는 Speed 값을 넣어 주면 된다. 

Locomotion State machine & Transition
Main State machine 및 흐름도

밑의 사진은 Animation Blue print 안의 Event graph에서 실제로 캐릭터가 움직임에 따라 캐릭터의 스피드를 구하는 Blue print이다. 

 

일주일은 7일이고 1월1일부터 날짜를 세서 7로 나눈 나머지를 요일로 선택하게 하는 방법으로 하기로 했다.

이때 1월1일이 금요일이라서 요일 배열의 1번째 값을 금요일로 해두었다.

#include <string>
#include <vector>

using namespace std;

string solution(int a, int b) {
    string answer = "";
    int month[]={31,29,31,30,31,30,31,31,30,31,30,31};
    string day[]= {"THU","FRI","SAT","SUN","MON","TUE","WED"};
    int sum=b;
    for(int i=0;i<a-1;i++){
        sum+=month[i];
    }
    
    answer=day[sum%7];
    return answer;
}

 

처음에는 둘다 2진수로 바꾸고 비교를 해보면 되겠다고 생각을 했는데 비트연산자를 사용해서 미리 연산을 해두고 2진수 변환을 하면 더 좋다는 걸 알게되어서 그렇게 구현해 보았다

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

vector<string> solution(int n, vector<int> arr1, vector<int> arr2) {
    vector<string> answer;
    int a,b;
    for(int i=0;i<n;i++){
        string tmp="";
        arr1[i]=arr1[i] | arr2[i];      //비트 OR연산해서 하나라도 1이면 1 
        
        while(tmp.size()!=n){       //2진수변환과 동시에 지도 만들기
            if(arr1[i]%2==0){
                tmp.push_back(' ');
            }else{
                tmp.push_back('#');
            }
            arr1[i]/=2;
        }
        reverse(tmp.begin(),tmp.end());
        answer.push_back(tmp);
    }
   
    return answer;
}

 

유클리드 호제법을 통해 최대공약수를 구할 수 있다 

유클리드 호제법

2개의 자연수에 대해서. a 를 b로 나눈 나머지를 r 이라고 한다면. ( 단, a > b )

a와 b의 최대 공약수는 b와 r의 최대공약수와 같다.

이 성질에 따라 b를 r로 나눈 나머지를 구하는 과정을 반복하여.

나머지가 0이 되었을 때, 나누는 수가 a와 b의 최대 공약수다.

#include <string>
#include <vector>

using namespace std;

int gcd(int a,int b)            //유클리드 호제법
{
    if(b==0) return a;
    else return gcd(b,a%b);     
}

vector<int> solution(int n, int m) {
    vector<int> answer;
    
    int gcd_num = gcd(max(n,m),min(n,m));
    
    answer.push_back(gcd_num);
    answer.push_back((n*m)/gcd_num);                //최소공배수 = 기존 두 수의 곱 / 최대공약수
    return answer;
}

 

다리가 견딜 수 있는 무게만큼을 검사하고 순서대로 트럭을 보내면 된다. 

만약 그 다음 트럭이 견딜 수 있는 무게 이상이 된다면 0인 트럭을 추가해서 그 전 트럭을 계속 이동시켜주면 된다.

#include <string>
#include <vector>
#include <queue>
//1.초를 어떻게 셀것인가 -> 무게가 0인을 자동차를 추가하자 및 초 추가
//2.자동차무게의 합이 다리가 견딜 수 있는 무게보다 크다면? -> 0추가
using namespace std;

int solution(int bridge_length, int weight, vector<int> truck_weights) {
    int time = 0;
    queue<int> que;

    int weightSum = 0;        //자동차 무게합 
    int i = 0;                //인덱스
    while (1) {
        int curWeight = truck_weights[i];
        if (i == truck_weights.size()) {            //마지막 값만 남았을경우 
            time += bridge_length;                //다리길이 만큼 걸림
            break;
        }

        if (que.size() == bridge_length) {          //다리길이 만큼 이동했다면
            weightSum -= que.front();             //다리끝에 있는 자동차의 무게를 빼준다.
            que.pop();
        }
        if (weightSum + curWeight <= weight) {
            weightSum += curWeight;
            que.push(curWeight);
            i++;
        }
        else que.push(0);

        time++;
    }
    return time;
}

1.문자열 처리

● 숫자가 string 형태로 되어 있을때, int 로 바꾸고 싶다면 '0'을 빼주기

● .find 를 했을 때 못찾으면 string::npos(-1)을 반환

● .erase(index,size) 삭제할 index ~ size까지 size없으면 그자리에 글자 삭제

문자열 자르기

1. string 클래스 사용

#include <string>
using namespace std;

int main() {
   string lines = "hi,my,name,is";
   size_t previous = 0, current;
   current = lines.find(','); //구분자, 찾지못한경우 npos반환

   while(current !=string::npos) {
       string substring = lines.substr(previous, current - previous);
       cout << substring << " ";
       previous = current + 1;
       current = lines.find(',', previous);
   }
   cout << lines.substr(previous, current - previous);
   return 0;
}

 

2. sstream 클래스 사용

 stringstream ss;                    //빈칸을 경계로 string 나눔
 for (auto record : records) {
     ss.str(record);             //records에서 record string 형태로 가져오기
     string time, number, status;  //record string에서 나눠줄 변수들 
     ss >> time >> number >> status;

     parks[number].push_back(time);
     ss.clear();
 }

 

2.vector

vector 선언 및 초기화

#include <vector>

int main(){
	vector<int> v;			//벡터 선언
    vector<int> v(n,x);		//x로 n개의 원소 생성한 벡터 선언
}

 

vector 요소 삭제

#include <vector>

int main(){
	v.erase(v.begin()+index)		//index번째의 원소 삭제
    v.erase(v.begin() + s, v.begin() + e) //s 부터 e-1까지의 인덱스 삭제
}

 

vector 삽입

v.insert( v.begin(), 100 ); // 100을 제일 처음에 추가
v.insert( v.begin()+5, 5 ); // 5를 (처음+5)에 추가
v.insert( v.end(), 9 ); // 9를 제일 마지막에 추가
v.push_back(5); 5를 제일 마지막에 추가

 

2. map

※ map vs unordered_map

vs map  unordered_map
정렬 오름차순 자동 정렬 정렬안함
중복허용 비허용 비허용
탐색속도 O(logN) O(1)
문자열 길이가 길고 데이터가 크지 않을 때 보다 유리 보다 불리

 

map 삽입

um[변수명]= 변수값;

 

반복문 순회

for(auto it: um)
{
  // key-value의 특성에 따라 접근하면 된다.
  std::cout << "key  : " << it->first << std::endl; // 문자열 A, B 출력
  std::cout << "value: " << it->second << std::endl; // 정수형 1111, 2222 출력
}

그동안 협업으로 프로젝트를 진행 해본적은 있지만 애자일방법론 같은 정말 협업에 사용되는 방법을 따라 개발해본적은 없어 이번 프로젝트를 통해 기획단계에서 부터 클래스 다이어그램, 시퀸스 다이어그램, 요구사항 명세서등을 어떻게 작성하는 지 익히고 사용해보기로 하였다.

1. 클래스 다이어그램

클래스 다이어그램의 정의를 보자

클래스 다이어그램은 구조 다이어그램으로 클래스 내부 구성요소 및 클래스 간의 관계를 도식화하여 시스템의 특정 모듈이나 일부 및 전체를 구조화한다.

클래스 다이어그램에서 클래스는 이름, 속성(변수), 메소드 순으로 나열하고 속성과 메소드 사이에 선을 그어 분류한다.

변수 선언

+ public
-  private
#  protected 
~  default 
{readonly} final
밑줄 static
[*] or [0..1] 리스트 사이즈 

 

클래스 간 관계

이미지 참조:  https://en.wikipedia.org/wiki/Class_diagram

●Association

Association은 참조 관계일때 사용하고  A -> B 이면 A가 B를 참조 A - B 이면 A가 B를 B가 A를 참조할 수 도 있고 둘 다 참조 이거나 둘 다 참조가 아니다.

 

Inheritance

Inheritance는 상속관계로 부모 클래스와 자식 클래스 간의 상속관계에 사용

 

Realization

Realization은 인터페이스를 상속하여 클래스에서 실제 기능을 구현할 때 사용

 

Dependency

Dependency는 클래스간의 참조관계에서 사용하지만 Association은 변수로 다른 클래스와 연관이 있을 때 사용하지만 Dependency는 메소드의 파라미터나 반환에 사용되는 클래스 관계에서 사용

 

Aggregation

Aggregation은 집합관계에서 사용

 

Composition

Aggregation과 비슷하게 전체 - 부분의 집합 관계를 나타낼 때 사용하지만 Aggregation 보다는 더 강력한 집합을 의미할 때 사용합니다. 합성 관계에서는 부분이 전체에 종속적이고 라이프 사이클을 관리한다 라고 볼 수 있다.

 

#include <string>
#include <vector>

using namespace std;
//4개의 블록을 파악 -> 제거 -> 밑으로 이동-> 다시 파악-> 반복
void fillEmpty(int m,int n,vector<string>& board){  //빈공간을 내려주는 함수
    for(int j=0;j<n;j++){
        string tmp="";
        for(int i=m-1;i>=0;i--) if(board[i][j]!= ' ') tmp+=board[i][j];     //채워져있는 칸이라면 tmp에 모두 추가
        int i=m-1;          //젤 끝에서부터
        for(auto c: tmp) board[i--][j]=c;       //tmp에서 하나씩 꺼내서 젤밑에서부터 추가
        for(;i>=0;i--) board[i][j]= ' ';         //나머지는 빈칸으로
    }
    
}

bool is_erase(int i,int j,vector<string>& board,vector<vector<bool>>& vis){
    if(board[i][j]!=board[i+1][j]||
      board[i][j]!=board[i][j+1]||
      board[i][j]!=board[i+1][j+1]) return false;
    vis[i][j]=true;
    vis[i+1][j]=true;
    vis[i][j+1]=true;
    vis[i+1][j+1]=true;
    return true;
}
int solution(int m, int n, vector<string> board) {
    int answer = 0;
    vector<vector<bool>> vis(m,vector<bool>(n,false)); //2차원 bool벡터 선언 및 초기화
    
    while(1){
        bool done=true;
        fill(vis.begin(),vis.end(),vector<bool>(n,false)); //2차원 bool 벡터 초기화
        for(int i=0;i<m-1;i++){         //비교를 위해 m-1,n-1까지
            for(int j=0;j<n-1;j++){
                if(board[i][j]!= ' ' && is_erase(i,j,board,vis)) done = false;
                //공백이 아니고 지울 수 있다면 방문체크 및 계속진행
            }
        }
        if(done) break;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(vis[i][j]) board[i][j] = ' '; //방문처리한=> 즉 지울 수 있는 칸은 지운다.
            }
        }
        fillEmpty(m,n,board); //빈공간 내리기
    }
    for(auto s:board){
        for(auto c: s) if(c== ' ') answer++; //빈칸인 경우 지워진블록으로 추가
    }
    
    return answer;
}

문제설명이 길지만 요약해보자면 채팅방에 나가고 들어오는 것을 기록하고 아이디에 대한 닉네임은 제일 마지막에 사용자가 등록한 것으로 사용한다. 

일단 공백을 기준으로 문자열을 나누는것이 중요하다. 

문자열을 나누는 방법은 나는 sstream을 자주 사용해서 이 방법을 같이 첨부한다.

※sstream 활용

stringstream ss;
 for (auto reco : record) {
     ss.str(reco);			//record배열에서 하나씩 가져와서 string 형태로 변환
     string status, id, name;			//빈칸을 기준으로 나눌 단어를 넣을 변수
     ss >> status >> id >> name;		//string 형태로 변환된 단어를 분할하여 변수에 차례로 저장
     ss.clear();						//ss을 초기화하여 다음 요소를 분할할 수 있도록 함.
 }

 

이후 map을 사용하여 기록된 아이디와 닉네임을 추가하는 방법까지는 생각했는데 이 다음이 잘 생각나지않았는데 힌트를 보니 굳이 map에서 전부다 계산하는 것보다 행동과 아이디를 나누어서 나중에 더해주는 방식이 좋을 것 같아 그렇게 해보았다.

전체코드

#include <string>
#include <vector>
#include <map>
#include <sstream>
using namespace std;
//닉네임은 맵을 통해 최신화해주기
//나머지는 어떻게 저장해둘것인가..
//->따로 배열을 만들어서 저장하는게 좋겠다=> state배열 answer에는 id만 저장해서 나중에 합쳐주기
vector<string> solution(vector<string> record) {
    vector<string> answer, state;
    map<string, string> m;
    stringstream ss;
    for (auto reco : record) {
        ss.str(reco);
        string status, id, name;
        ss >> status >> id >> name;
        ss.clear();
        if (status == "Enter") {
            state.push_back("님이 들어왔습니다.");
            answer.push_back(id);               //id를 저장해서 나중에 state와 map을 합치기
            m[id] = name;
        }
        else if (status == "Leave") {
            state.push_back("님이 나갔습니다.");
            answer.push_back(id);
        }
        else {
            m[id] = name;
        }


    }

    for (int i = 0; i < answer.size(); i++) {
        answer[i] = m[answer[i]] + state[i];
    }
    return answer;
}

 

+ Recent posts