*오늘의 코드카타*
주어진 수가 1이 될 때까지 입력된 수가 짝수라면 2로 나누고, 입력된 수가 홀수라면 3을 곱하고 1을 더한 후 결과로 나온 수에 같은 작업을 1이 될 때까지 반복한다면, 이 작업을 몇 번이나 반복해야 하는지 반환하는 함수 solution을 완성해주세요. 단, 주어진 수가 1인 경우에는 0을, 작업을 500번 반복할 때까지 1이 되지 않는다면 -1을 반환해 주세요.
입력된 수, num은 1 이상 8,000,000 미만인 정수입니다.
#include <string>
#include <vector>
using namespace std;
int solution(int num) {
int answer = 0;
long long n = num;
if (n == 1) {
return 0;
}
while (n != 1 && answer < 500) {
if (n % 2 == 0) {
n /= 2;
} else {
n = (n*3) + 1;
}
answer++;
}
if (n == 1) {
return answer;
} else {
return -1;
}
}
콜라즈 추측에 관한 문제이다.
어렵게 생각하지 말고, 어떤 조건을 만족할 때까지 반복해야 하는 경우 while을 쓰는 게 편하다.
또한 num을 입력받더라도 중간 계산 과정에서 int 값을 초과할 수 있어서 long long n = num; 을 사용했다.
*그 외에 오늘 한 것*
[프로그래머스]
수열과 구간 쿼리 4
먼저 const auto& range for 문을 사용해서 배열을 순회하면서 i를 k로 나누어 나머지가 0일 때 arr[i]에 1을 더하는 식으로 구성했다.
#include <string>
#include <vector>
using namespace std;
vector<int> solution(vector<int> arr, vector<vector<int>> queries) {
for (const auto& query : queries) {
int s = query[0];
int e = query[1];
int k = query[2];
for (int i = s; i <= e; i++) {
if (i % k == 0) {
arr[i]++;
}
}
}
return arr;
}
다르게 구성해보자면 다음과 같은 방법이 있다. 처음에 s, e, k 를 쓰지 말고 q[0], q[1]. q[2] 를 직접 사용하는 것이다.
#include <string>
#include <vector>
using namespace std;
vector<int> solution(vector<int> arr, vector<vector<int>> queries) {
vector<int> answer;
for(auto q:queries){
for(int i = q[0]; i<=q[1]; i++){
if(i%q[2] == 0){
arr[i]++;
}
}
}
return arr;
}
배열 만들기 2
내 아이디어 : 벡터 원소를 문자열로 변환해서 find 함수 사용해서, 0과 5를 찾고 없으면 -1이 담긴 배열을 return.
(1) to_string을 사용해서 문자 0, 5만 있는지 확인 후 있으면 배열에 i 값 추가하기
#include <string>
#include <vector>
using namespace std;
vector<int> solution(int l, int r) {
vector<int> answer;
for (int i = l; i <= r; i++) {
string strNum = to_string(i);
bool isZeroFive = true;
for (char c : strNum) {
if (c != '0' && c != '5') {
isZeroFive = false;
break;
}
}
if (isZeroFive) {
answer.push_back(i);
}
}
if (answer.empty()) {
answer.push_back(-1);
}
return answer;
}
(2) 10으로 나누면서 나머지 값으로 숫자 찾아내기
이 경우에는 10으로 나누면서 나머지가 0또는 5가 아닌 순간 그냥 바로 반복문을 탈출하도록 만들어야한다.
#include <vector>
using namespace std;
vector<int> solution(int l, int r) {
vector<int> answer;
for (int i = l; i <= r; i++) {
int temp = i;
bool isOnlyZeroAndFive = true;
while (temp > 0) {
int digit = temp % 10;
if (digit != 0 && digit != 5) {
isOnlyZeroAndFive = false;
break;
}
temp /= 10;
if (isOnlyZeroAndFive) {
answer.push_back(i);
}
}
if (answer.empty()) {
answer.push_back(-1);
}
return answer;
}
(3) **접근 방식 바꾸기(5와 0으로 이루어진 숫자를 먼저 찾고 조건에 맞는지 체크하고 넣기)**
즉, queue를 활용한 BFS 방식을 적용해보자.
0과 5를 하나씩 넣어보면서 l~r 까지의 값에 해당하면 그 값을 반환하는 방식이다.
초기값을 0과 5로만 이루어진 자연수 중 가장 작은 수인 5를 큐에 넣고 시작하고,
범위를 초과하는 것을 막기 위해서 가지치기를 실시한다. -> if(n>r) continue;
그리고 이미 가지치기를 해놨으므로 if(n >= l ) 조건만 만족하면 정답 배열에 추가한다.
이후, 가지치기를 해놓은 상태에서 q.push(n*10);, q.push(n*10 + 5); 이렇게 현재 숫자 n의 뒤에 0과 5를 붙인 새로운 숫자 2개를 만들어서 큐의 대기열에 추가한다. -> 조건문을 만족하는 것만 통과시켜서 정답 배열에 추가한다.
카운트 업
#include <string>
#include <vector>
using namespace std;
vector<int> solution(int start_num, int end_num) {
vector<int> answer;
for (int i = start_num; i <= end_num; i++) {
answer.push_back(i);
}
return answer;
}
단순한 문제여서 그냥 풀었는데, 다른 분들의 풀이를 보니까 신기(?)한게 있어서 좀 더 알아보았다.
#include <bits/stdc++.h>
using namespace std;
vector<int> solution(int start, int end) {
vector<int> answer(end - start + 1);
iota(answer.begin(), answer.end(), start);
return answer;
}
1. #include <bits/stdc++.h> : C++의 거의 모든 표준 라이브러리(vector, string, queue, numeric 등등...)를 한번에 불러오는 만능 헤더 파일. 보통 알고리즘 문제를 풀면 이것 저것 추가할 헤더들이 많을 때 관행적으로 사용한다.
2. vector<int> answer(end - start + 1); : 벡터의 메모리 재할당 과정을 아예 생략해서 속도를 더 빠르게 할 수 있도록 메모리를 재할당 하는 과정이다.
3. iota 함수 : iota(이오타)는 라이브러리에 포함된 함수로, 지정된 구간을 특정 시작점부터 1씩 증가하는 값으로 쫙 채워주는 역할을 한다. push_back을 써서 하나씩 내가 채우는게 아니라, 그냥 처음값, 끝값 줄테니까 엑셀마냥 + 마우스 눌러서 쭉 드래그 한다고 생각하면 된다.
콜라츠 수열 만들기
#include <string>
#include <vector>
using namespace std;
vector<int> solution(int n) {
vector<int> answer;
answer.push_back(n);
while (n != 1) {
if (n % 2 == 0) {
n /= 2;
}
else {
n = 3 * n + 1;
}
answer.push_back(n);
}
return answer;
}
배열 만들기 4
문제 읽는 연습용으로 좋은 거 같다. 처음에 읽는데 뭔말인지를 몰랐다. 이런 경우에는 입출력 예를 보고 직접 넣어보면서 시뮬레이션 해보는 게 좋은 것 같다.
배웠던 개념인 stack을 사용해서 풀 수 있다.
#include <string>
#include <vector>
using namespace std;
vector<int> solution(vector<int> arr) {
vector<int> stk;
int i = 0;
while (i < arr.size()) {
if (stk.empty()) {
stk.push_back(arr[i]);
i++;
}
else {
if (stk.back() < arr[i]) {
stk.push_back(arr[i]);
i++;
}
else {
stk.pop_back();
}
}
}
return stk;
}
이 코드는 알고리즘에서 자주 쓰이는 단조 증가 스택을 만드는 과정이다.
stk.empty() : 스택이 비어있는 경우 무조건 현재 원소인 arr[i]를 넣고, 다음 원소를 확인하기 위해 i를 1 증가시킨다.
stk.back() < arr[i]: 스택의 가장 마지막 원소가 새로 들어올 값보다 작다면 오름차순이 유지되므로, 값을 그대로 스택에 넣는다.
stk.back() >= arr[i] : 스택의 마지막 원소가 새 원소보다 크거나 같으면 오름차순을 유지하기 위해 기존 원소를 빼버린다. 그리고 다음번 while 루프에서 같은 arr[i]를 가지고(i가 증가되지 않았음) 스택의 다음 마지막 원소와 다시 크기를 비교한다.
간단한 논리연산
#include <string>
#include <vector>
using namespace std;
bool solution(bool x1, bool x2, bool x3, bool x4) {
bool answer = true;
answer = (x1 | x2) & (x3 | x4);
return answer;
}
논리 연산자를 사용하면 그 결과는 true or false라는 점을 이용. 문제에서 주어진 연산자를 그대로 옮겨 적은 것과 같다.
주사위 게임 3
문제를 보고서는 우선 주사위를 4번 굴려서 같은 수가 몇개 나왔는지 세어보고 그 케이스별로 분류하여 계산 공식을 작성하기로 했는데, 코드 작성이 너무 길어지고 알아보기도 어려워서 다른 방향으로 수정했다. 컨테이너와 정렬 방식을 활용하는 편이 더 좋아보였다. 그리고, 절대값이 나오니까 애초에 배열안에 값을 집어넣을 때 오름차순으로 정렬되도록 sort()를 사용했다.
(정렬 방식)
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
int solution(int a, int b, int c, int d) {
vector<int> v = {a, b, c, d};
sort(v.begin(), v.end());
if (v[0] == v[3]) {
return 1111*v[0];
}
else if (v[0] == v[2]) {
int p = v[0];
int q = v[3];
return (10*p + q) * (10*p + q);
}
else if (v[1] == v[3]) {
int p = v[3];
int q = v[0];
return (10*p + q) * (10*p + q);
}
else if (v[0] == v[1] && v[2] == v[3]) {
int p = v[0];
int q = v[2];
return (p+q) * abs(p-q);
}
else if (v[0] == v[1]) {
return v[2] * v[3];
}
else if (v[1] == v[2]) {
return v[0] * v[3];
}
else if (v[2] == v[3]) {
return v[0] * v[1];
}
else {
return v[0];
}
}
근데 조금 더 고민해보니까 정렬하면서 중복된 값을 제거하는 성질을 이용하면 좀 더 쉽게 구할 수도 있지 않을까? 생각도 해보았다.
그런데 알고보니, 반복되어 나온 주사위의 숫자를 count 하고, 해당 count 를 이용해서 문제를 풀어나가는 굉장히 좋은 방법이 있었다. count를 기준으로 주사위 숫자를 그룹화하는 table 배열을 사용하는 것이다.
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
int solution(int a, int b, int c, int d) {
int answer = 0;
vector<int> table(6);
table[a-1]++; table[b-1]++; table[c-1]++; table[d-1]++;
vector<vector<int>> nums(5);
nums[table[0]].emplace_back(1);
nums[table[1]].emplace_back(2);
nums[table[2]].emplace_back(3);
nums[table[3]].emplace_back(4);
nums[table[4]].emplace_back(5);
nums[table[5]].emplace_back(6);
if(!nums[4].empty())
{
return 1111*nums[4][0];
}
else if(!nums[3].empty())
{
return pow(10*nums[3][0]+nums[1][0],2);
}
else if(nums[2].size() == 2)
{
return (nums[2][0]+nums[2][1])*abs(nums[2][0]-nums[2][1]);
}
else if(nums[2].size() == 1)
{
return nums[1][0]*nums[1][1];
}
else
{
return *min_element(nums[1].begin(),nums[1].end());
}
return answer;
}
1. 주사위 숫자가 각각 몇 번 나왔는지 table에 기록한다. table[a-1]++; table[b-1]++,....
2. vector<vector<int>> nums(5); // nums[table[i]].emplace_back(1~6); : 어떻게 이런 생각을 할 수 있었을까. 하는 포인트였다.
nums의 인덱스 번호는 주사위가 나온 횟수(0~4)를 의미하게 되고, 각각의 nums에는 해당 횟수만큼 나온 주사위 숫자들이 들어가게 된다.
이렇게 분류를 미리 해버린니까 뒤에서 반복문을 쓸 필요가 없어져 버린다.
3. nums 배열에서 각각의 경우에 수에 맞는 값만 꺼내어서 answer를 반환한다. nums[4] 부터 출발해서 비어있으면 nums[3]으로 가고, ... 결과적으로 아무것도 해당되지 않으면 가장 작은 값을 찾는 방식으로 진행한다.
4. 여기서 push_back을 사용해야겠다고 생각하기 쉬운데, 코드에서 값을 벡터에 넣을 때 push_back 대신 emplace_back을 사용하는 이유가 있다. int 자료형같은 단순한 자료형에서는 성능이 거의 동일하지만 점차 복잡한 자료들을 다룰 수록 emplace_back으로 객체를 다루는 것이 유리하다. 어려운 말은 일단 제쳐두고 복사/이동 연산을 생략할 수 있어서 성능상 push_back 보다 유리하다고 이해했다.
글자 이어 붙여 문자열 만들기
#include <string>
#include <vector>
using namespace std;
string solution(string my_string, vector<int> index_list) {
string answer = "";
for (int idx : index_list) {
answer += my_string[idx];
}
return answer;
}
단순한 문제였다. 이제 반복문 사용해서 하는 거는 어느정도 익숙해진 것 같다.
9로 나눈 나머지
#include <string>
#include <vector>
using namespace std;
int solution(string number) {
int answer = 0;
int sum = 0;
for (char c : number) {
sum += c - '0';
}
answer = sum % 9;
return answer;
}
요새 자주 보는 녀석. 문자에서 '0'을 빼면 고유의 숫자 값이 나온다.
문자열 여러 번 뒤집기
처음에는 while문으로 두 숫자 사이를 좁혀가면서 두 숫자의 위치를 바꿔가며 구현하려고 했다.
(1) while문을 이용한 구식 방법
#include <string>
#include <vector>
using namespace std;
string solution(string my_string, vector<vector<int>> queries) {
string answer = "";
for (const auto& query : queries) {
int s = query[0];
int e = query[1];
while (s < e) {
char temp = my_string[s];
my_string[s] = my_string[e];
my_string[e] = temp;
s++;
e--;
}
}
answer = my_string;
return answer;
}
(2) C++의 swap 이용하기
#include <string>
#include <vector>
using namespace std;
string solution(string my_string, vector<vector<int>> queries) {
string answer = "";
for (const auto& query : queries) {
int s = query[0];
int e = query[1];
while (s < e) {
swap(my_string[s], my_string[e]);
s++;
e--;
}
}
answer = my_string;
return answer;
}
(3) C++의 reverse 이용하기
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
string solution(string my_string, vector<vector<int>> queries) {
string answer = "";
for (const auto& query : queries) {
int s = query[0];
int e = query[1];
reverse(my_string.begin() + s, my_string.begin()+e+1);
}
answer = my_string;
return answer;
}
[atanny]
UPROPERTY 매크로의 주요 역할 : 가비지 컬렉션 대상으로 등록, 에디터에서 편집 가능, 시리얼라이제이션 지원
UPROPERTY, UFUNCTION, USTRUCT, UENUM 매크로
BlueprintCallable 지정자
UPROPERTY 없이 선언된 UObject 포인터 : 가비지 컬렉션 시스템에 등록되지 않아 메모리 누수 발생 위험 있음.
BlueprintImplementableEvent, BlueprintNativeEvent
[C++ 프로젝트]
Text RPG 팀프로젝트 -> Github Destktop 사용(branch, pull request, sln 에러 잡기) Item.h, Item.cpp 작성(Item 클래스), 포션 사용 함수 use(), int count; 추가 후 count--; 사용으로 포션 차감 추가, 포인터 사용하고 Character.cpp에 use(), rand()(+<cstdlib> 함수 사용해서 랜덤으로 두 포션 중 하나를 소모하도록 구성, inventory.erase(inventory.begin() + randomidx); 를 이용해서 포션 남은 개수가 0이면 벡터형 인벤토리에서 제거.
this는 C++에서 "지금 이 코드를 실행하고 있는 객체 자기 자신의 주소"를 의미하는 포인터.
<개발자들의 약속 : 코드 표기법>
ItemManager 헤더 : 파스칼 [클래스 이름, 타입 이름]
1. 모든 단어의 첫 글자를 대문자로 시작하기.
2. 단어 사이에는 공백이나 특수 문자가 없고, 단어들을 연결하여 작성하기.
itemManager 지역 변수 : 카멜 [변수 이름, 메서드 이름, 클래스 이름, 파일 이름]
1. 단어의 첫 글자는 소문자로 시작하기.
2. 두 번째 단어부터는 첫 글자를 대문자로 표기하기
3, 단어 사이에는 공백이나 특수 문자가 없고, 단어들을 연결하여 작성하기
total_score 변수 : 스네이크 [변수 이름, 함수 이름, 데이터베이스 테이블, 열의 이름]
1. 단어와 단어 사이를 밑줄로 구분하고 각 단어는 소문자로 작성하기
2. 주로 데이터베이스에서 사용된다. 데이터베이스와 어플리케이션 코드 간의 일관성을 유지하기 위해 사용!
데일리 스크럼(오전 : 전날 뭐했는지, 마무리 시간 : 오늘 뭐할건지 짧게 공유하고 + 기록 하였음)
오후 (7시~) 팀원들과 작성한 코드 보면서 리뷰 및 피드백 -> 깃허브에서 main branch로 푸시 하기 전에 다함께 코드 확인.
트러블 슈팅 : 조건식의 오류를 발견, 필수 과제 완성 이후 추가 기능 부재
해결 : 팀원과 문제 공유 후 더 나은 개선 방안으로 수정하였음. 추가 기능 아이디어 제안(상점, 인벤토리, 확률 및 강화 로직) + 개인적으로는 사용자 경험 개선을 위한 필터 기능 구현을 추가.
칸반보드 -> 트렐로에서 소규모 프로젝트는 무료. 주문표 쳐내는 거 시각화 한것(할일 목록을 최대한 잘게 쪼개기)
클래스 다이어그램, 시퀀스 다이어그램
다 같이 스크럼하면서 서로의 오류 크로스 체크. 다같이 집중해서 밀도있게 검사할 수록 더 좋은 아이디어도 나오고 오류도 금방 수정이되고, 개선안도 창의적으로 나올 수 있을 것 같다.
(26.05.22) 팀원들과의 커뮤니케이션, 노션을 이용한 팀프로젝트 관리, Github Desktop + Github 팀 프로젝트 경험을 할 수 있었다.
[C++ 공부(?)]
팀프로젝트 진행 과정 중 복습 : 배열 함수, 조건문 작성, 연산자, 조건문, 배열과 함수, 포인터와 레퍼런스, 클래스 개념, 객체지향 프로그래밍, 객체지향적 설계
visual studio 폴더 정리 방법(#include "../"Entity.h" 상위 폴더 지정), 폴더 변경 시 sln 에러 해결
github push 할 때 한글 깨지는 문제 --> 작성 중인 소스 파일의 인코딩을 UTF-8로 변경한 뒤 다시 커밋하고 푸시 (visual studio가 한글 윈도우에서 기본 인코딩이 다르게 설정되어있는 경우가 많기 때문.
C+++ 코딩 표준(언리얼 엔진)
클래스 체계, 저작권 고지 방법
명명 규칙 : 대문자, 접두사, 해당 타입에 적합한 접두사, 매크로는 모두 대문자로 구성되고 단어를 언더스코어로 분리 + 접두사 UE
변수, 메서드, 클래스 이름은 명확하고 확실하고 내용을 파악할 수 있어야 함. 과도한 약어는 피할 것.
포용적인 단어를 선택하고 전문적인 언어를 사용하기 위해 노력할 것.
표준 라이브러리의 사용 : <atomic>, <type_traits>, <initializer_list>, <regex>, <limits>, <cmath>, <cstring> : memcpy(), memset()
const의 정확한 사용 방법
'TIL' 카테고리의 다른 글
| 05.29 TIL (ch.2 팀프로젝트 day6) (0) | 2026.05.29 |
|---|---|
| 05.28 TIL (ch.2 팀프로젝트 day5) (0) | 2026.05.28 |
| 05.27 TIL (ch.2 팀프로젝트 day4) (0) | 2026.05.27 |
| 05.26 TIL (ch.2 팀프로젝트 day3) (0) | 2026.05.26 |
| 05.21 TIL (ch.2 팀프로젝트 day1) (0) | 2026.05.21 |