그래픽스 파이프라인의 각 과정의 설명 간단하게 적으려고 한다.
코드나 추가내용이 필요한 부분은 따로 파트별로 글을 작성하려고 한다.
이 글에는 코드의 내용은 없으면 역할 정도만 간단한 설명을 하고있다.


Graphics Pipeline(그래픽스 파이프라인)

더보기
그래픽스 파이프라인

3차원 이미지를 2차원 래스터 이미지로 표현을 하기 위한 단계적 방법

그래픽스 렌더링 파이프라인, 렌더링 파이프 라인 이라고도 불리며 
코딩을 하는순서가 아님!!, 컴퓨터 내에서 그래픽을 생성하는 진행도를 나타냄

그래픽스 파이프라인 진행도에 따른 폴리곤 생성 과정

주황색 박스 : 조작가능(Programmable)한 단계를 나타낸다.
파이프라인 옆에 있는 그림처럼 각 진행도처럼 적용된다.

그래픽스 파이프라인에서 작업되는 진행도(레스터라이즈 이후에는 색을 칠한다)

 

Raster(래스터) :

컴퓨터에서 어떠한 도형(또는 이미지)를 2차원 배열 형태의 픽셀로 구성하여 이 점들의 모습을 조합, 

일정한 간격의 픽셀들로 하나의 Scene(장면)을 표현하는 것 

 

//그래픽스 파이프라인 참고사이트 

https://learn.microsoft.com/ko-kr/windows/uwp/graphics-concepts/graphics-pipeline https://m.blog.naver.com/jyh0841/220473615493

Input Assembler(IA, 입력 조립기)

더보기

 

vertex버퍼의 정점(vertex) 데이터를 이용하여 파이프라인에서 사용할 primitive(프리미티브)로 조립
vertex버퍼의 data는 좌표,uv좌표,색상등 여러가지 data값이 있다.

 

p.s primitive(프리미티브) : 이용 가능한 가장 단순한(작은) 처리 단위 

Vertex Shader(VS, 정점 셰이더)

더보기

조립된 primitive를 이용하여 모든 vertex에 대한 연산 처리

-Shape Transform(공간변환) 작업 수행

-많은 비용이 드는 작업이기 때문에 GPU에서 처리

Shape Transform(공간변환) 과정

더보기

World Transform(월드 변환)Local / Model Space(로컬/모델 공간)에서 World Space(월드 공간)으로 변환

3D월드(필드)로 Mesh를 옮기는 과정 , 행렬(Matrix) 변환이 필수

행렬을 이용하여 Translate(이동),Rotate(회전),Scalling(크기) 변환을 수행

 

World Matrix(월드 행렬) = Scale Matrix(크기 행렬) * Rotation Matrix(회전 행렬) * Translation Matrix(이동 행렬)


 

Shape Transform 의 World Transform  진행도

Local/Model Space : 3D 오브젝트 모델을 처음 생성할 때에 모델을 가장 편리한 방법으로 배치
편리한 방법 = 오브젝트(도형) 자신의 기준점을 원점
ex) 구를 만들 땐 구의 중심의 좌표를 (0,0,0)으로 설정하고, 반지름을 간단하게(반지름 =1) 설정

World Space : 각각의 오브젝트(도형)들이 월드의 원점을 기준으로 하고

오브젝트의 상대적 위치를 표현하기 위한 좌표계(공간) 이다.

-월드 좌표계에는 한 개의 원점이 존재한다.
-월드 좌표계는 한 개 이상 존재할 수 있으며 , 월드 좌표계가 다르면 오브젝트끼리 서로 침범 할 수 없다.

 

월드 변환이 필요한 이유 : 각각의 오브젝트를 월드변환을 거치지 않고 World Space에 배치하면 오브젝트끼리 겹치하는 현상이 발생


따로 글 작성 예정 / 필요내용 : 게임수학 - 행렬 계산 방법 , 변환 행렬 들 작성  

(링크 달 공간)

Tesslator(테셀레이터)

더보기

폴리곤 안에 vertex를 추가하여 굴곡(곡면)을 주는 작업

-안티앨리어싱과 유사함(똑같은건 아님)

-이 과정은 생략이 가능

View Transform(뷰 변환)

더보기

월드 상의 오브젝트들을 쉽게 관찰하기 위한 작업

World Space(월드 공간)의 오브젝트들을 view / camera space (카메라 공간)으로 변환 하는 작업 

월드에 배치된 오브젝트들을 관찰점(카메라 좌표) 기준으로 변환한다.

 


따로 글 작성 예정 / 필요내용 : 카메라 오브젝트(객체) 만드는 코드 등 

(링크 달 공간)

Projection Transform(투영)

더보기

3차원(n차원) 2차원(n-1차원)으로 변환 

그림 출처 : https://mooneegee.blogspot.com/2015/02/directx9-world-transform-viewing.html

그림처럼 3D의 빨간 구와 노란 구는 near clip plane으로 투영되서 2D 이미지가 되었다.
위의 구성은 먼 거리(빨간 구)는 크게 , 가까운 거리(노란 구)는 작게 구성을 한다.
단 Near와 Far Clip의 비율은 같아야한다. 

원근감을 살릴려면 빨간구를 작게 노란 구가 커야된다고 생각하지만

그림 출처 : https://zamezzz.tistory.com/98

가까운 쪽을 늘리면서 원근감을 만들것이기 떄문에 상관이 없어진다


그림 출처 : http://www.songho.ca/opengl/gl_projectionmatrix.html

녹색 구는 Clipped 지역에 있으므로 녹색 구는 Clip Space 밖에 있어 투영되지 않는다. 

-Clip Space(클립 공간)은 Frusturm(절두체)이 정의 ( Clip Space = Frusturm )
-Clip Space는 NDC(Normalize Device Coordinate)라고도 불리며, 이 공간은 투영(Proection) 행렬에 정의

 

따로 글 작성 예정 / 필요내용 : 투영 행렬 작성

(링크 달 공간)


p.s NDC(Normalize Device Coordinate) = 정규 좌표

: 정규 좌표(NDC)는 1을 기준으로 하는 2차원 좌표, 좌표계의 원점은 화면의 점 중앙( 0,0)

NDC (Normalize Device Coordinate)

이 공간안에 있는 것들은 투영하고 밖에 있는것은 제외하게 된다 

정규 좌표를 사용하면 해상도에 따른 화면좌표 계산이 단순화 됨, 해상도를 제외한 값을 계산해 두면 해상도가 변할 경우 쉽게 실제 화면 계산이 가능해진다.

즉, 화면 해상도 차이에 빠르게 적응하기 위한 하나의 도구입니다.

Geometry Shader(GS, 기하 도형 셰이더)

더보기

처리된 vertex 데이터에 입력 받은 정보를 이용하여 파티클,털,모션블러 등등의 기하처리를 한다.

PS(Pixel Shader)에서 사용할 보간 작업을 수행

 

p.s 보간

: 새로운 점을 만들기 위해 수많은 점들을 평균화시키는 것, 즉, 두 점을 연결하는 방법을 의미한다.

여기서 말한 연결은 궤적을 생성한다는 뜻이다.

예를 들어 컴퓨터에서 선을 그릴때 ,선은 무수히 많은 점들을 그려 선을 만드는 것이다.

하지만 이 점들을 전부 메모리로 가지고 있으면 메모리 낭비가 심하기 때문이다.

그렇기 때문에 보간작업시  두 점(vertex)을 이용하여 선의 색을 보간에 맞춰 작업 하고 그린다.

포물선 같은경우는 3개의 점이 필요로 하다.

보간에 관련하여 참고한 사이트
https://m.blog.naver.com/ojh6t3k/20196349385

Rasterizer(RS,레스터라이저)

더보기

PS(pixel shader)를 작업하기 위해 준비를 갖추는 과정
-하드웨어 자체 알고리즘을 통해 동작

-Pixel Shader의 호출 방법을 결정
-Pixel Shader 단계를 위한 기본 요소를 준비

Rasterize(레스터라이즈) 과정

: 레스터라이즈 진행시에 작업되는 내용들을 정리했다.


Light(조명

더보기

조명을 비추어 빛이 물체에 닿았을 때 물체의 표면으로부터 반사되는 색상

빛이 월드 공간에 적용됐을 때 물체가 반사하려는 색상을 결정

 

빛을 적용시키려면 빛의 반사 벡터를 알아야되는데 , 이 내용은 나중에 게임수학을 다루면서 적도록 하겠다.

(링크 적용)

 

1. 빛의 요소 : 

1-1.Spcular Light(정반사광) : 한 방향으로만 반사되는 빛 , 입사각과 반사각이 똑같은 특징을 갖고있음

1-2.Diffues Light(난반사광) : 물체의 표면에 반사되어 여러 각도로 반사되는 빛 

1-3.Ambient Light(환경광) : 다른 표면에 반사되어 전반적인 장면을 밝게하는 빛 

빛의 요소 3가지를 간략히 표혀한 그림

 

2.광원

2-1. Direction Light(방향성 광원) : 위치는 가지지 않음, 지정된 방향으로 평행하게 빛을 발산 . ex)태양

2-2. Point Light(점 광원) : World Space내에 위치를 가지며 모든 방향으로 빛을 발산 ex)성냥불,전구

2-3. SpotLight(스팟 광원) : 광원은 위치를 가지며 특정한 방향으로 원뿔형태의 빛을 발산 ex)손전등


이 요소들을 구조체로 보면 아래와 같은 코드로 적용이 된다.
//Material 구조체에 포함되어있는 빛의요소 data
struct Material
{
	XMFLOAT4 ambient;
	XMFLOAT4 diffuse;
	XMFLOAT4 specular;
	float specularPower;
};

//Light 구조체 , Material을 멤버변수로 가지고 있다.
struct Light
{
	XMFLOAT4 camPosition; // 카메라의 위치(빛 위치)
	XMFLOAT4 light_color; // 빛의 색
	XMFLOAT4 light_direction; // 빛의 방향 
	XMFLOAT4 globalAmbient; //월드 환경광 -> material과 합쳐져서 유저한테 보여진다.
	Material material;
}

 Material 안에 Light(빛의 요소)들이 적용되어 색상을 반사하게 되는것이다. 

코드 중의 specularPower는 정반사광의 날카로운 정도를 지정하는 변수이다.(값이 높을수록 하이라이트를 강조)

 

Light를 적용시 Material은 반드시 필요한 존재가 된다.

 

Back-face Culling(후면 컬링)

더보기

레스터라이저에서 세팅한 전면 그리기 방식 방향을 이용하여 보이지 않는 뒷면을 렌더링 및 연산에서 제외

사진 예시가 이런것 일뿐 , 사실 후면도 전면이다, 카메라 시 점을 180도로 돌리면  물체의 뒷면도 그려야되기 때문

Cliping(클리핑)

더보기

Clip Space(클립 공간)에서 벗어난 요소를 렌더에서 제외(렌더링에서 제외 될뿐 연산은 유지)

Cliped 부분을 제외하게 된다.

Frustum Culling(절두체 컬링)

더보기

-완전한 내부 : 폴리곤이 완전히 절두체 내부에 위치하면 보존 
-완전한 외부 : 폴리곤이 완전히 절두체 외부에 위치하면 제외 
-부분적 내부 : 폴리곤이 부준적으로 절두채 내부에 위치하면 두 부분으로 나누어 내부에 위치한 부분은 보존하고 나머지는 제외 

이 절두체 컬링은 cliping과 비슷해보이지만 다른 개념이다.

Cliping은 렌더링 되는 객체들을 화면에 표시하기 전에 잘라내는 과정을 말하는것이고 
Frustum Cilling은 카메라의 시야(Frustum)에 포함되지 않는 객체를 제거하는 과정이다. 

하지만 두 공통점은 렌더링에서는 제외지만 연산은 적용이 된다는 점이다. 

Culling 과 Cliping의 차이

더보기
 
 
컬링 
클리핑 
의미 
보이지 않는 폴리곤은 렌더링 및 연산에서 제외 
보이지 않는 폴리곤은 렌더링에서 제외 
연산 
제외 
실행 
정확도 
낮다 
높다 
속도 
빠르다 
느리다 

ViewPort(뷰포트)

더보기

ClipSpace(투영공간)의 좌표를 스크린(우리가 보는 화면) 좌표로 변환 

-투영된 정보를 Screen에 출력할 수 있는 크기로 변환 

-클라이언트 Clip Space를 출력할 영역을 설정

뷰포트 예시

Pixel/Fragment Shader (PS, 픽셀 셰이더)

더보기

각 픽셀의 데이터를 생성

-입력 값(Textuer)을 결합하여 색상을 결정

-기본 요소에 대한 보간된 데이터를 받아 색상과 같은 픽셀 데이터를 생성(Resterizer 단계에서 가져온 data)

-조명 및 픽셀에 대한 후 처리 가능

-CPU는 Pixel 각각 한개의 전부 접근해서 작업하지만 GPU는 병렬작업이 가능하기때문에 PS는 GPU로 사용됨

Output Merger(OM ,병합 출력기)

더보기

다양한 유형의 출력 데이터(Pixel Shader값 ,Z/Stencil buffer 정보)를 렌더링 대상Z-buffer,Stencil_buffer의 내용과 결합하여 최종 파이프라인 결과를 생성한다. 

 

https://school.programmers.co.kr/learn/courses/30/lessons/172928

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것

 

<나의 풀이 (비추천)>

더보기

park의 위치를 문자열로 준것을 보고 2차원 배열을 이용하자 생각했고

routes의 문자열로 방향과 이동거리를 주는것을보고 문자열을 쪼갤때 많이 쓰는 <sstream>헤더를 활용하자 생각을 하였다.

 

또한 각 방향에 따라 수치들이 변경되는게 많아 define을 사용하여 매크로 상수화 했다.

(enum 쓰는게 가독성이 올라가는데 문제라서 그냥 define으로 하였다.)

 

이동 할떄 사용한 정보(경로)를 기록할 필요가 없어 전부 for 반복문 안에 지역변수로 활용하였다.

 

이 문제의 제일 중요한 것은 '강아지 이동'을 시킬때 조건 2가지가 중요하다

조건 1. 경계선이 벗어나면 다음 명령을 실행하고, 경계선 안에 있으면

조건 2. 이동 범위 안에 장애물이 있는지 확인을 하고 장애물이 있으면 다음 명령을 실행하게 해놨다.

 

조건1,조건2를 전부 만족할경우 현재 강아지의 좌표를 변경해주는 식으로 진행하였다.

 

간략한 내용은 플로우차트에 적어놨다.

플로우 차트 밑에 기능적으로 함수화를 하면 가독성이 많이 올라간다.

<플로우 차트>

0. 사전 준비
0-1. Park 지도를 만들기 (2차원 배열화)
0-1-1. Park 지도를 만들면서 (시작지점과 , 장애물지점을 설치)
(// 0:길 1:장애물 2:로봇강아지 )

1.routes 리스트의 data를 사용할수 있는 형식으로 만들기
1-1.방향(E,W,N,S) 와 이동거리(Move) 형식으로 만들기

2.강아지 진행시키기
(조건 1: 최종 위치기 경계선을 벗어나면 , 명령 동작x)
(조건 2: 이동경로에 장애물이 있는경우, 명령 동작x)

#include <string>
#include <vector>
#include <sstream>

#define UP_OR_LEFT -1
#define DOWN_OR_RIGHT 1

#define MOVE_X 1
#define MOVE_Y -1

#define PARK_ROAD  0
#define PARK_OBJECT 1
#define PARK_DOG 2

using namespace std;

vector<int> solution(vector<string> park, vector<string> routes)
{
    vector<int> answer;

    int width = park[0].length();
    int height = park.size();

    int dog_x =0, dog_y =0; // 로봇강아지 현재좌표 

    //0-1. Park 지도를 만들기 (2차원 배열화)
    vector<vector<int>> Park_map(height,vector<int>(width,0));

    //0-1-1. Park 지도를 만들면서 (시작지점과 , 장애물지점을 설치)
    // (0:길 1:장애물 2:로봇강아지 )
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            //시작 지점 기록
            if (park[y][x] == 'S')
            {
                dog_x = x;
                dog_y = y;
                Park_map[y][x] = 2;
                
            }
            
            //장애물 위치 기록
            else if (park[y][x] == 'X')
            {
                Park_map[y][x] = 1;
            }
        }
    }

    int routes_roop = routes.size();

    for (int route_index = 0; route_index < routes_roop; route_index++)
    {
        //1.routes 리스트의 data를 사용할수 있는 형식으로 만들기
        // 1 - 1.방향(E, W, N, S) 와 이동거리(Move) 형식으로 만들기
        char dir;
        int move = 0;
        stringstream stream;
        stream.str(routes[route_index]);

        stream >> dir;
        stream >> move;
        //------------------------------------------------------------

        /* 2.강아지 진행시키기
        (조건 1: 최종 위치기 경계선을 벗어나면 , 명령 동작x)
        (조건 2: 이동경로에 장애물이 있는경우, 명령 동작x) */
        int dir_add = 0; // 방향 이동시 -,+ 결정 
        int move_axis = 0; // 이동하는 축 결정 
        bool move_check = true;

        switch (dir)
        {
        case 'E' :
            dir_add = DOWN_OR_RIGHT;
            move_axis = MOVE_X;
            break;
        case 'W':
            dir_add = UP_OR_LEFT;
            move_axis = MOVE_X;
            break;
        case 'S':
            dir_add = DOWN_OR_RIGHT;
            move_axis = MOVE_Y;
            break;
        case 'N':
            dir_add = UP_OR_LEFT;
            move_axis = MOVE_Y;
            break;
        }
        
        int move_result = move * dir_add; //이동거리 * 방향결정 = 강아지가 움질익 방향 

        int move_min, move_max,move_tmp; // 이동 범위
        
        switch (move_axis)
        {
        case  MOVE_X:
            //(조건 1: 최종 위치기 경계선을 벗어나면 , 명령 동작x)
            if (dog_x + move_result >= width || dog_x + move_result < 0)
            {
                break;
            }
   
            
            //이동 범위 계산
            move_min = dog_x;
            move_max = dog_x + move_result;

            if (move_min > move_max)
            {
                move_tmp = move_min;
                move_min = move_max;
                move_max = move_tmp;
            }

            //(조건 2: 이동경로에 장애물이 있는경우, 명령 동작x
            for (int i = 0; i < move_max - move_min; i++)
            {
                int move_x = dog_x + (dir_add * (i + 1));
                if (Park_map[dog_y][move_x] == PARK_OBJECT)
                {
                    move_check = false;
                    break;
                }
            }

            if (move_check)
            {
                Park_map[dog_y][dog_x] = PARK_ROAD;
                dog_x += move_result;
                Park_map[dog_y][dog_x] = PARK_DOG;
            }

            break;

        case  MOVE_Y:
            //(조건 1: 최종 위치기 경계선을 벗어나면 , 명령 동작x)
            if (dog_y + move_result >= height || dog_y + move_result < 0)
            {
                break;
            }

            //이동 범위 계산
            move_min = dog_y;
            move_max = dog_y + move_result;

            if (move_min > move_max)
            {
                move_tmp = move_min;
                move_min = move_max;
                move_max = move_tmp;
            }

            //(조건 2: 이동경로에 장애물이 있는경우, 명령 동작x
            for (int i = 0; i < move_max - move_min; i++)
            {
                int move_y = dog_y + (dir_add * (i + 1));
                if (Park_map[move_y][dog_x] == PARK_OBJECT)
                {
                    move_check = false;
                    break;
                }
            }

            if (move_check)
            {
                Park_map[dog_y][dog_x] = PARK_ROAD;
                dog_y += move_result;
                Park_map[dog_y][dog_x] = PARK_DOG;
            }

            break;
        }
    }

    answer.push_back(dog_y);
    answer.push_back(dog_x);
    return answer;
}

<다른사람의 풀이>

더보기

다른분이 풀은것을 보니 2차원 배열화를 할 필요가 없었다.

장애물때문에 2차원 배열하는게 좋을줄알았는데 그냥 park의 문자열 찾기로 x만 제외했으면 됬다.

 또한 나같은경우는 매번 roop를 돌때마다 switch문을 들어가서 각 해당되는값을 정의했는데

들어가기전 필요한 data들을 세팅하고 반복문을 돌리는 형식이다. 

 

<플로우 차트>

0. 사전 준비

0-1.dx,dy,mapping으로 각 방향시 이동하는 칸수 및 단일문자에 따른 인덱스값 설정

0-2. 시작 위치 좌표 알기 ( Park 반복문)

1. 강아지 이동경로 갯수만큼 roop

1-1. 강아지의 이동칸수 만큼 roop

1-1-1.강아지가 한칸 씩 이동하면서 장애물 체크 및 경계선 체크를 동시에 한다. 

2.반복문이 roop를 다 돌은경우 현재 좌표 갱신 

#include <string>
#include <vector>
#include <map>

using namespace std;
using pi = pair<int, int>;

//0 = S, 1 = N , 2 = E , 3 = W
int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, 1, -1 };

map<char, int> mapping = {
    {'E', 0}, {'W', 1}, {'S', 2}, {'N', 3}
};

vector<int> solution(vector<string> park, vector<string> routes)
{
    int cx, cy;

    //시작 위치 좌표 알기 
    for (int i = 0; i < park.size(); ++i) {
        for (int j = 0; j < park[i].size(); ++j) {
            if (park[i][j] == 'S') 
            {
                //tie : pair,tuple으로 구성 되어있을때, 여러개읜 return값이 필요할때 사용하는 명령어
                tie(cx, cy) = { i, j };
                break;
            }
        }
    }

    //강아지 경로 갯수만큼 반복문 roop
    for (int i = 0; i < routes.size(); ++i) {
        int num = routes[i][2] - '0'; // 이동거리 확인 , 문자열의 2번재 인덱스(숫자) ,char 자료형이기떄문에 아스키코드(0,-48)을 해서 int로 변경
        int dir = mapping[routes[i][0]];//이동방향 단일문자 확인 , map에 키값을 넣고 int 값을 받아옴 

        int nx = cx, ny = cy;
        while (num--) {
            nx += dx[dir];
            ny += dy[dir];

            if (!(nx >= 0 && nx < park.size()) || !(ny >= 0 && ny < park[0].size())) break; // 경계선 체크
            if (park[nx][ny] == 'X') break; // 장애물 체크
        }

        //해당 i의 이동roop를 다 돌은경우 좌표 갱신
        if (num < 0) {
            cx = nx;
            cy = ny;
        }
    }
    return { cy, cx };
}

 

 

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV.2 최솟값 만들기  (0) 2023.06.09
LV.1 내적  (0) 2023.06.09
LV.1 없는 숫자 더하기  (0) 2023.06.09
[LV2] 귤 고르기  (0) 2023.05.31
[LV1] 대충 만든 자판  (0) 2023.05.28

여러 API에서 제공하는 함수를 사용할때 매개변수를 문자열이나 단일문자로 인자값을 넣을때 함수의 자료값을 보고 멀티바이트인지 유니코드인지 헷갈릴때가 종종 생겨서 내가 정리하는겸 만들었다. 내용은 정말 간략하게 정리했다.

 

틀린 내용이 있거나 알고있으면 좋은 정보가 있으면 댓글 부탁드립니다. 


문자집합(Character Set) 정의

더보기

문자 집합은 정보를 표현하기 위한 글자나 기호들의 집합을 정의한 것

 

문자나 기호의 집합을 컴퓨터에 저장하거나 통신에 사용할 모적으로 부호화 하는 것을 문자인코딩(부호화)이라

하고 인코딩 된 문자부호(Character Set)를 다시 디코딩(복호화)하여본래 문자나 기호로 표현 할 수 있습니다.

 

문자의 크기(Size)

더보기

8bit = 1byte

Char 단일문자 자료형 크기가 1byte (0~ 255)다.

 

아스키 코드 1Byte
영어,숫자 등등 (반각문자) 1Byte
한글,일본어 등등 (전각문자) 2Byte

 

중국어 같은경우 획수가 많은건 3Byte도 있지만 그런건 예외이기 떄문에 위에 내용만 알고있으면 된다. 

 

ASCII(아스키코드)

더보기
아스키코드표

ASCII(American Standeard Code for Information Interchage) 의 약자다.

아스키 코드표는 용량이 1byte이지만
실제로 7bit까지만 써서 0~127(2^7)의 숫자를 나타낸다.

 

그리고 아스키코드 이후에 ISO/IEC 8859-1, ISO/IEC 8859-N 문자집합의 등장으로

기존 아스키코드 0~127은 그대로 사용하고 8bit까지 추가로 사용하게되며  128 ~ 255(2^8) 영역까지 사용하여 서유럽어에서 필요로 하는 문자와 몇가지 특수문자를 추가했다한다.

 

MultiByte(멀티바이트)

더보기

MBCS(Multy Byte Character Set) 의 약자이며 가변 너비 인코딩이라고한다.

 

다국어를 표한하기 위해 사용했던 방식의 문자 집합중 하나

문자의 크기에서 영어,숫자는 1byte 한국어,일본어는 2byte라고 소개했는데 이 부분을 해결해주는것이 멀티바이트 문자집합이다. 

 

간단히 말해서 입력값으로 들어오는 단일문자나 문자열을 자동으로 문자 종류에 따라 byte를 지정해주는 것이다. 

BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString);
//(hdc,x 좌표 , y 좌표, 문자열 , 문자열 길이)
//매크로 함수이며 문자열인자값을 무엇을 넣는냐에 따라 그에 맞는 함수가 호출된다.

//인자값 : 멀티바이트 문자열(LPCSTR)
BOOL TextOutA( _In_ HDC hdc, _In_ int x, _In_ int y, _In_reads_(c) LPCSTR lpString, _In_ int c);
//멀티바이트 문자나 문자열을 사용시 함수명뒤에 A가 붙는다.

 

UniCode(유니코드)

더보기

Universal Coded Character Set의 약자이며, 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준이다.

 

위에서 설명한 문자의 크기가 언어 종류에 따라 달랐는데 유니코드는 이런 가변적인 특성을 없애고 모든 문자를 2byte로 표현하기 위해 정의한 방식이다. 즉, 모든 문자를 2byte로 일관되게 지정하고 사용한다는것이다.

 

BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString);
//(hdc,x 좌표 , y 좌표, 문자열 , 문자열 길이)
//매크로 함수이며 문자열인자값을 무엇을 넣는냐에 따라 그에 맞는 함수가 호출된다.

//인자값 : 유니코드 문자열(LPCWSTR)
BOOL TextOutW( _In_ HDC hdc, _In_ int x, _In_ int y, _In_reads_(c) LPCWSTR lpString, _In_ int c);
//유니코드는 함수명뒤에 W(wide)를 붙인다. 또한 자료형에 W를 추가한다.(LPCWSTR)
//멀티바이트 문자열을 대입할때 "ABC123가나다" 이렇게 따옴표 안에 내용을 넣어서 사용한다.
//유니코드는 문자열 앞에 L을 추가하고 내용을 적는다. ex) L"ABC123가나다"

 

문자 집합에 따른 문자열 길이 계산시 유의사항

더보기
char* Ch = "멀티1234"; //멀티바이트 문자열 초기화
wchar_t* wCh = "유니1234" //유니코드 문자열 초기화

int len_Multi = strlen(Ch); //멀티바이트 문자열 길이 리턴
int size_Multi = sizeof(char) * Ch; //멀티바이트 문자열 크기 리턴

int len_Uni = strlen(wCh); //유니코드 문자열 길이 리턴
int size_Uni = sizeof(wchar_t) * wCh; //유니코드 문자열 크기 리턴

/* 결과값 */
// len_Multi = 8 : 한글은 (2글자 * 2byte), 숫자는 (4글자 * 1byte), 총 8글자.
// size_Multi = 8 : 한글은 2byte로 취급,숫자는 1byte취급 ,총 8byte.
// len_Uni = 6 : 유니코드는 전부 2byte로 취급, 전부 1글자 취급 ,총 6글자
//  size_Uni = 12  : 유니코드는 전부 2byte로 취급(6글자 * 2byte), 총 12글자

//p.s 멀티바이트 문자열 계산함수는 _mbslen() 함수를 이용해야 함.

 

프로젝트의 문자집합 확인 및 변경하기

더보기

컴파일러가 지정된 문자집합을 사용하게 하는 설정이다.

프로젝트> 속성 > 구성 속성 > 고급 > 문자 집합 >문자집합 변경

프로젝트의 문자 집합 변경

 

 

글 작성자가 공부하고 배운것을 간략하게 정리하는 페이지 입니다.

필요한 정보가 있으시면 확인하시고 만약 잘못된 내용이 있거나 이런 내용이 추가됬으면 좋겠다라면 댓글 부탁드립니다.

감사합니다.

 


좌표계

더보기

x,y,z 축이 있는 좌표계를 사용,

왼손좌표계 :
-D3D(DirectX3D)에서 사용되는 좌표계
-MS사에서 제작한 API, 윈도우 최적화
-윈도우 개발환경(빌드) 인경우 개발시에 많이 사용됨

 

오른손 좌표계:

-OpenGL(Open Graphics Library)에서 사용되는 좌표계

-멀티 OS 환경에서 사용(멀티플랫폼) -> 범용성은 좋으나, 최적화는 떨어짐

좌표계에 따라 Z축의 방향이 달라진다.

Z축의 방향이 중요한 이유 : 어떤 방향으로 그릴지의 기준이 되기 떄문!!

 

Vertex(정점)

더보기

말그대로 꼭지점, 물체의 기본을 이루는 원소와 유사함

Polygon(폴리곤)을 이루는 삼각형의 꼭지점

Vertex는 다양한 정보를 가지게 할 수있으며 , 그 정보를 토대로 그래픽스 작업을 한다.

 

ex) Vertex에 담겨있는 데이터들

-좌표계의 좌표 -> x,y,z,w

w=1 이동변환의 영향O

w=0 이동변환의 영향 X, 노멀값(노멀벡터)과 같은 방향 벡터일 경우

(w는 위치좌표 -> 3차원은 4x4 행렬을 취함)

-회전 각도 -> x,y,z,r (축을 중심으로 회전,각도 )

-UV좌표계 -> u,v ( 범위가 0,0 ~ 1,1인 좌표계, u = x , v= y)

-색깔(color) -> r,g,b,a(RGB값 알파블렌드(투명도))

... API에서 제공하는 Vertex가 있을수도 있고 개발자가 만들어 원하는 값으로 쓸 수도 있다.

 

Polygon(폴리곤)

더보기

3D 컴퓨터 그래픽에서 면의 조합으로 물체를 표현할 때의 각 요소

각 요소는 삼각형을 취하고 있음, 원자(Vertex)들이 모여져 만든 분자와 유사함

즉, 삼각형 하나가 폴리곤이라고 생각하면 편하다.

 

p.s 왜 Polygon의 각 요소는 삼각형을 취하고 있는가??

▶삼각형은 곡면(구체)를 만들기에 최적화가 되어있기 떄문이다.

 

Mesh(메시)

더보기

다면체의 형태를 구성하는 폴리곤의 집합 (실제로 사용자들이 눈으로 보는 물체)

Polygon의 집합체, Mesh

 

Vertex,Polygon,edge 및 Mesh가 전부 포함

 

Modeling(모델링)

더보기

컴퓨터가 이해할 수 있는 형태로 Mesh등의 정보를 저장한 데이터

프로그래머들이 사용하는 요소, 아트 계열 프로그래머들을 모델러 라고 부르는 이유이다.

 

Vertex를 이용하여 삼각형을 그릴때 필요한 개념

더보기
<왼손좌표계>시작좌표는 원하는 위치로 잡고 시작하면된다. 3차원 좌표계이기 떄문에 뒷면도 존재

Vertex의 인자값(좌표값)을 입력순서에 따라 폴리곤 그리는 방향이 정해진다

왼손 좌표계(D3D) : 시계방향, 시계방향으로 그려진 면이 앞면
오른손 좌표계(OpenGL) : 시계반대 방향, 시계반대 방향이 앞면

 

이 방향으로 인하여 왼손 좌표계 <> 오른손 좌표계 전환에 활용 할 수 있다.

즉,D3D에서 빌드하던것을 가져와서 설정값(vertex순서 및 좌표 등등)을 변경 후 OpenGL환경에서 사용할 수 있는것이다.

 

Polygon의 앞면,뒷면 선정이 중요한 이유 

더보기

유저들이 보는 면은 앞면일 확률이 높다. 그러므로 앞면에 이미지를 그리는것이 일반적이다.

그럼 뒷면에도 그림을 그려야 하는가? 아니다.

 

개발시에 그림을 그리는 작업의 처리비용이 매우 높기 때문이다.

그런 이유로 앞면을 정해서 앞면만 그리고 뒷면은 그리지않아 처리비용을 줄이려고 하는것이다.
(일반적으로 그런거지, 뒷면도 그리게 할수 있음)

 

일반적으로 게임에서 벽 텍스처를 뚫고 지나가면 뒷면에 아무것도 없는 경험이 보통 이런경우이다.

 

Rendering(렌더링)

더보기

월드 상에 모델링을 이용하여 만들어진 장면(Scene)을 컴퓨터가 조명의 배체와 면의 특성, 기타 다른 설정들을 바탕으로 계산하여 그림을 생성

렌더러는 2D의 이미지를 1장의 사진(그림)을 찍는는 개념이다.
이 이미지들을 이제 연속으로 찍어 보여주는것이 애니메이션이다.

레이아웃 : 물체(Object)를 작업 공간에 배치

애니메이션 : 배치된 물체의 움직임을 설정

 

Texture(텍스처)

더보기

3차원 물체의 표면 2차원 이미지를 입혀서 적은 폴리곤으로도 높은 디테일을 표현 할 수 있게 해주는 렌더링 관련 요소, 텍스처의 좌표축은 UV좌표계를 사용한다.

 

UV좌표계 : 0,0 ~ 1,1의 크기를 가지고있는 좌표계,
이유 : 텍스처 이미지를 로드할때 비율,사이즈 등 각각 다르기때문에 UV 좌표계 기준으로 변환한것이다. 

UV좌표계를 이용하여 이미지를 텍스처 맵핑 하는 간략한 과정

 

Suface(서피스)

더보기

이미지 데이터를 저장하는데 사용되는 버퍼, 화면에 출력되는 면

즉 2D이미지 버퍼 ( 텍스처파일은 원래 2D이고 3차원 물체이 입히는 형식)
서피스는 WINAIP의 backDC와 유사하다 

(backDC 개념은 더블버퍼링에서 나온 것, 더블버퍼링도 화면에 출력 할 이미지를 저장 후 사용하는 용도이다.)
서피스는 보통 폴리곤의 앞면,뒷면의 서피스를 각각 갖고있다.

 

2023.06.05

수정: 컴퓨터 그래픽스에서 말하는 Surface는 버퍼이다. 
(버퍼 : 메모리(data)를 저정하는 공간)

취소선의 내용은 내가 똑같은 Surface라고 정리를 했는데, 
폴리곤의 surface는 그대로 '표면'이란 뜻으로 사용된거다.

폴리곤 앞면(front surface),뒷면(back surface)

즉, 여기서 설명한건 버퍼의 역할이기 때문에 잘못 설명한것이 된다. 

 

 

Culing(컬링)

더보기

렌더링 작업 중 렌더링 할 필요가 없는 요소들을 선별하여 제외하는 기법
가시성 선별(Visibility Culing)기법이라고도 불린다.

 

cut에서 가져온 단어 -> 이미지의 필요없는 부분을 자른다는 뜻

ex) surface는 앞면,뒷면을 다 가지고 있지만 뒷면은 그릴 필요가 없기에 제외할때 사용됨

 

Anti-Aliasing(안티 앨리어싱)

더보기

이미지의 픽셀계단처럼 출력되는것을 앨리어싱이라 하며, 이 계단현상을 없애기 위해 생겨난 기법

비교사진

기법으로는 슈퍼샘플링,멀티샘플링 등이 있다.(여러 종류가 있음)
슈퍼 샘플링 : 처리비용이 높음, 효율이 좋음
멀티 샘플링 : 처리비용이 낮, 효율이 낮음

 

Projection(투영)

더보기

간략히 말하면 n차원을 n-1차원으로 바꾸는 것,

투영 개념은 투영행렬 및 선형대수가 필요한 개념이기에 따로 정리가 필요하다.

Frustum(절두체)를 설명하려면 필요한 개념이기에 간략히 적어놓았음

 

Frustum(절두체)

더보기

투영행렬(Projection Matrix)을 계산할 때 범위의 모양이 사각뿔의 머리를 잘라 둔 것처럼 생겨서 절두체라고 한다.
카메라의 크기와 범위 모양

절두체 = 카메라 영역 = 렌더링 영역

: 카메라 앵글(유저의 시점)밖에 있는건 찍지 않고 카메라에 담긴 영역(렌더링하는 영역)을 그려야되기 떄문에 영역(절두체)를 알아야 한다.

카메라의 시점이 사각뿔 모양처럼 생겼고 이걸 렌더링하는것이 절두체이다.

그래픽스파이프라인의 Projectino Transforn(투영변환)을 공부할때 더욱 자세하 알수 있을것이다.

 

Z-buffer(깊이 버퍼), Stencil Buffer(스텐실 버퍼)

더보기

Z-buufer : 화면에 그려진 각 픽셀의 Z(깊이) 값을 저장,
절두체에서 픽셀의 깊이 값을 기록하는데 사용(원근감)

Frusturm의 화면을 렌더링하였을때 : (왼)겹치는 영역을 표시 (오) Z-buffer를 적용하여 원근감을 준 화면

각각 다른 Z값(깊이)를 가지고 있는 물체들이 존재 할때  카메라시점으로 보면 A,B의 겹치는 영역이 존재한다.
Frusturn(절두체)에서 기록한 Z값을 이용하여 A,B의 겹치는 영역중 가까운 영역부터 그려서 원근감을 부여한다.


Stencil Buffer : 특정 영역이 렌더링 되는것을 막기위해 사용되는 버퍼 

FPS 게임중 조준 하는 경우 (왼)적용되기전 (오)Stencil버퍼를 적용 후

위에 사진처럼 렌더링 하기전 Stencil buffer의 데이터를 이용하여 렌더링에서 제외하는것이다.
예시로는 저격총의 에임, 거울에 비춘 물체 시점(거울 안에서만 비춰야되고 거울 밖은 비추면 안되는상황)
스파이/잡임 게임에서 플레이어의 시점으로만 조명이 비추고 나머지는 어둡게하는 상황을 표현할때도 사용됨

 

이 두 버퍼는 렌더링 되기전에 사용되는 버퍼이며 이 두 버퍼는 개발시에 보통 한번에 만들어서 한번에 작업처리를 한다. 

 

Shader(셰이더)

더보기

3D컴퓨터 그래픽에서 최종적으로 화면에 출력하는 픽셀의 색 정해주는 함수

색의 농담,색조,명암 효과를 주는 주체 

그래픽스파이프라인에서 VertexShader,PixerShader에 대해 배울때 자세히 알수있기때문에 간략히 적어놓았다.

 

회전(rotation)

더보기

▶Euler Angle(오일러 각)

3차원 공간의 절대적 좌표를 기준으로 물체의 회전을 측정하는 방법
X,Y,Z 을 기준으로 회전하기 때문에 직관적이고 조작이 용이하다.

180도가 넘는 회전의 표현이 가능하다.

3번에 나누어 계산하기 때문에 비용이 크며 Gimbal Lock(짐벌 락)이 발생할 수 있다.

 

간단히 말하면 X축 회전 계산, Y축을 회전, 계산 Z축을 회전 계산 계산이 많이 들수 밖에 없다. 

 

▶Quaternion(쿼터니언)

방향회전을 표현한다.

4개의 성분(x,y,z,w)로 이루어져 있으며 각 성분은 축이나 각도가 아니다.

벡터(x,y,z : 방향)와 스칼라(w:roll,회전)를 의미한다.

180도가 넘는 회전을 표현하지 못한다. (대처가능 : 210도 회전 = 179도 회전 + 31도 추가회전)

각 축을 한번에 계산하여 비용이 적고 Gimbal Lock(짐벌 락)이 발생하지 않는다.

 

Gimbal Lock(짐벌 락): 두개의 회전 축이 서로 겹쳐지는 현상, 축이 3개가 되고 나서 발생한 문제

축이 겹쳐지면서 하나의 축이 제 역할을 못하게 되는 현상 
Euler (gimbal lock) Explained 이 영상 한번 보면 이해가 쉽게 됩니다. 

 

회전은 자세히 들어가면 사원수라는 수학 개념이 들어가기 때문에
나중에 수학을 다뤄볼때 자세하 적어보겠다.


D3D에서는 회전을 표현하는 용어가 따로 있다.

X축 : pich(피치)

Y축 : yaw(요)

Z축 : roll(롤)

D3D에서 D3DQuaternionRotaionYawPitchRoll()를 이용하여 쿼터니언 회전이 가능

 

Material(재질)

더보기

조명을 비추어서 빛이 물체에 닿았을 때 물체의 표면으로부터 반사되는 색상

빛이 월드 공간에 적용 됐을 때 물체가 반사하려는 색상을 결정

빛(조명)이 적용될때 반드시 Material(재질)이 필요하다.(중요)

 

Amibient(환경광) Diffues(난반사광) Specular(정반사광) 을 다 합치면 최종적으로 우리가 보는 물체가 나온다.

위 이미지에서 나온내용은 빛(조명)에 관련된 내용이기 때문에 설명을 따로 하지 않겠다.

 

OverDraw(오버드로우)

더보기

렌더링의 단일 프레임에서 시스템이 화면에 한 픽셀을 여러 번 그리는 것 즉, 겹쳐서 그려진다는 뜻
처리비용이 높아지는 이유 중 하나 (문제점)

이미지 출처 :&nbsp;https://www.racketboy.com/retro/about-video-games-rasterization-and-z-buffer

이미지는 Z-Buffer를 설명하는 이미지이긴 하지만, 오버드로우를 표현하기에는 적절한 이미지여서 포함했다.
현재 이미지를 보면 한 픽셀에 S2 -> S1->S3 순서로 전부 한번씩 그려진후에 최종적으로 S3가 그려진다.
이 부분들은 S3만 그리게 되면 한번만 그리면 되는데 이런식으로 그리게 되면 총 3번의 작업이 되며 처리비용이 높아진다.


*오버드로우를 대처 및 수정하는 방법

1.레이아웃에서 불필요한 배경삭제

의미없는 배경들을 최대한 삭제로 오버드로우를 줄일수 있다.

 

2.뷰 계층 구조의 평면화

레이아웃들이 겹치면 안보이는데 그려야되는 경우가 생길수 있다.
그럼으로 이런 점을 고려하여 그리면 줄일 수 있다.

 

3.투명도 감소

화면에 투명 픽셀을 렌더링하는 것(알파렌더링)오버드로우의 주요 원인,

기존 픽셀에 먼저 그리고 투명 픽셀을 혼합하여 원하는 시각효과를 뽑아냄으로 오버드로우가 발생한다.

즉, 투명객체의 수를 줄이면 오버드로우를 어느정도 대처 할 수 있다.

ex)회색 텍스트를 출력하려 할때 , 검정색 텍스트 + 투명 픽셀(투명도 조절) 이게 아닌 회색으로 텍스트를 출력하면 더욱 성능개선이 된다는 뜻

 

Quad OverDraw(쿼드 오버드로우) [위에서 설명한 오버드로우랑은 다른 개념]

더보기

폴리곤을 그릴때 4개의 픽셀을 하나의 단위로 그

리는데, 이때 낭비되는 영역쿼드 오버드로우다.

회색 삼각형을 그릴때 녹색픽셀만 그리는게 아닌 빨간색 영역까지 그려지게 된다.&nbsp; (빨간색 영역 = 쿼드 오버드로우)

4개의 픽셀을 기준으로 그리는 이유는 텍스쳐 샘플링 떄문이다.
이 텍스처 샘플링이 부하가 큰 작업이다. 그래서 픽셀 수에 딱 맞는 해상도의 텍스처를 미리 여러개 준비해놓고 픽셀 수에 맞는 준비된 레벨의 텍스쳐만을 사용해서 렌더링을 하게된다.
이 작업을 텍스쳐의 mip level(밉 레벨) 이라고 한다. 

즉, mip level은 동일한 그림을 다양한 해상도의 사이즈로 텍스쳐를 준비하는 작업이다.

밉 레벨 계산은 4픽셀 씩 그리면서 ddx,ddy 미분을 해서 정확한 텍스쳐의 밉레벨 계산을 한다.


폴리곤의 크기(화면상의 크기)가 작을수록 쿼드 오버드로우가 심하게 발생된다.

가까이 있는 물체들은 그렇게 심하지 않지만 멀어지면 멀어질수록 쿼드오버드로우가 발생하고있다.

이미지의 빨간색이 쿼드오버드로우가 발생하고 있다는 뜻이며 
점점 멀어지게 되면 폴리곤은 한픽셀보다도 작아질수 있다.
픽셀보다 작은데 그 하나의 폴리곤을 나타내기 위해 4개의 픽셀을 사용하는것이다.

쿼드오버드로우를 줄이기 위하여 삼각형의 크기를 한 픽셀보다 더 작아지지 않게 하기 위한 기능이 바로 LOD 기법이다. (오버드로우 때문이 아님) 

쿼드오버드로우는 https://bbs.ruliweb.com/hobby/board/600001/read/1678 이 게시글을 참고하여 작성하였습니다.

 

https://school.programmers.co.kr/learn/courses/30/lessons/138476

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것 

 

<나의 풀이(비추천)>

더보기

플로우차트

1.귤을 size 기준으로 오름차순 정렬

2.오름차순 정렬한 귤을 반복문을 돌려 사이즈별로 갯수를 체크( vector<int> tangerine_count 에 저장)
3.정렬되서 나온 벡터를 귤 갯수 기준으로 내림차순 정렬

4.상자에 k갯수만큼 담으면서 루프 ( k개를 초과할시 루프 탈출후 answer 반환)

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

using namespace std;

int solution(int k, vector<int> tangerine) 
{
    int answer = 0, count = 0; //count : 상자에 들어가는 귤의 갯수 
    vector<int> tangerine_count; // 귤을 사이즈별로 나눠서 갯수를 알게하는 벡터
      
    sort(tangerine.begin(), tangerine.end()); //귤을 오름차순 순으로 정렬 

    int compare_tagerine = tangerine[0];//정렬한 귤 첫번째를 선택(기준)

	//루프를 시작 ( 사이즈별로 갯수 확인 루프 )
    for (int i = 1; i < tangerine.size(); i++)
    {
    	//기준 귤 사이즈 와 i번째 귤의 사이즈가 같으면 count++
        if (compare_tagerine == tangerine[i])
        {
            count++;
        }
        else // 기존 귤 사이즈와 i번째 귤이 다름
        {
            tangerine_count.push_back(++count); // 자기자신을 count하면서 벡터에 추가
            compare_tagerine = tangerine[i];//기준을 i번째 귤로 교체
            count = 0;//count 초기화 
        }
    }
    tangerine_count.push_back(++count); // 마지막 원소count 가 안된경우 추가 
	//마지막 루프를 탈출할때 count가 안되는 경우가 있기 때문에 따로 추가
    
    //사이즈 별로 나눠놓은 귤갯수 벡터를 정렬 후 내림차순 정렬로 전환 
    sort(tangerine_count.begin(), tangerine_count.end());
    reverse(tangerine_count.begin(), tangerine_count.end()); // 내림차순 정렬로 바꾸기 

    int count_sum = 0; // 상자에 들어간 귤 
    int i = 0;//인덱스
	
    //k갯수를 초과하지 않으면 계속 상자에 담음 
    while (k > count_sum)
    {
        //사이즈 별로 가장 많은 귤을 먼저 상자에 담음
        count_sum += tangerine_count[i++];
        answer++;
    }


    return answer;
}

<다른사람의 풀이>

더보기

벡터가 [ 1,3,2,5,4,5,2,3]인 경우

 

1.첫번재 루프

t = 귤 벡터의 첫번째인덱스의 데이터값
v[ 1 - 1 ] ++; 

v[0]은 1의 값을 가지고 있게 된다.
이 벡터의 의미는 Size가 1 인 귤을 count했다는 의미이다.

 

2.두번째 루프

t = 귤 벡터의 두번째인덱스의 데이터값

v[3-1]++; //v[2]은 Size가 3인 귤을 의미,count한다

...

 

루프를 계속 반복하면

결과:

v[0] = 1 // Size = 1 인 귤이 1개 있음,

v[1] = 2  // Size = 2 인 귤이 2개 있음,

v[2] = 2  // Size = 3 인 귤이 2개 있음,

v[3] = 1  // Size = 4 인 귤이 1개 있음,

v[4] = 1  // Size = 5 인 귤이 2개 있음,

이런식으로 나눌수 있다. 

#include <bits/stdc++.h>

using namespace std;

int solution(int k, vector<int> tangerine)
{
    int answer = 0;
    int m = *max_element(tangerine.begin(), tangerine.end()); //귤벡터중 제일 큰 원소(size)를 호출
    //m = 귤 최대 size를 입력 ,기준이 됨 
    vector<int> v(m, 0); // m범위 만큼 0(초기값)으로 초기화
    
    //위에 내용 참조
    for(auto& t : tangerine)
    {
        v[t - 1]++;
    }
    
    //사이즈별로 나눈 귤을 내림차순 정렬
    //rbegin,rend로 역으로 정렬
    //Stable Sorting은 같은 것이 있는 경우 정렬하기 전의 순서가 유지되는 정렬 알고리즘이다
    stable_sort(v.rbegin(), v.rend());
    
    //사이즈 별로 나눈 벡터 사이즈만큼 루프를 돌림
    for(int i = 0 ; i < v.size() ; i++)
    {
        answer++;
        k -= v[i]; // 현재 인덱스의 갯수만큼 상자에 담음
        if(k <= 0) return answer; // 상자에 k개 만큼 초과시 answer값을 반환
    }
    
    return answer;
}

 

 

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV.2 최솟값 만들기  (0) 2023.06.09
LV.1 내적  (0) 2023.06.09
LV.1 없는 숫자 더하기  (0) 2023.06.09
LV.1 공원산책  (0) 2023.06.08
[LV1] 대충 만든 자판  (0) 2023.05.28

https://school.programmers.co.kr/learn/courses/30/lessons/160586

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것 

 

<나의 풀이 (비추천)>

더보기

정답처리 된 코드지만 , 가독성이 안좋게 구성이 되었다.

하나의 target을 정한뒤 keymap을 벡터를 탐색해서 해당 문자를 찾는것으로 설계를 하다보니 반복문 다중으로 많이 사용되었음. 

 

플로우차트는 

0.target의 문자열을 길이기준으로 오름차순 정렬

ex) ABCD,ABC,ABCDE -> ABC,ABCD,ABCDE

1.비교할 target 선정 

1-1.target의 char(단일문자)하나를 선정
2.target_Char 와 keymap과의 비교 시작

2-1.keymap_char_index 설정 ( keymap 리스트에 공통적으로 적용되는 인덱스)

2-2. keymap_list Roop 시작

2-3.비교 시작  

2-3-1.(같은 문자를 찾은경우) 2-1로 돌아감,roop한 횟수를 answer에 저장

2-3-2.(같은 문자를 못 찾은경우) 2-1로 돌아감,,다시 탐색

2-3-3.(roop를 다 돌렸지만 해당 target 문자를 못찾을경우) answer 벡터에 -1 대입후 반복문 탈출 및 1번으로 돌아감

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

using namespace std;

bool compare(string keymap1, string keymap2)
{
    //글자갯수로 오름차순 정렬
    return keymap1.length() < keymap2.length();
}

bool char_compare(char target_char,vector<string> keymap,int c)
{
    for (int d = 0; d < keymap.size(); d++)
    {
    	//해당 target의 문자 index가 문자열 길이 보다 크거나 같을경우, 그 반복문 본문 스킵
        //ex)ABC(문자열 길이 3) targer index (4) -> skip 
        if (keymap[d].length() <= c)
        {
            continue;
        }

        char key_char = keymap[d].at(c);

        //2-3.비교시작
        if (target_char == key_char)
        {
            //2-3-1.(같은 문자를 찾은경우) 2-1로 돌아감,roop한 횟수를 answer에 저장
            return true;
        }

    }

    return false;
}

vector<int> solution(vector<string> keymap, vector<string> targets)
{
    //keymap 문자열 길이기준으로 오름차순 정렬 
    sort(keymap.begin(), keymap.end(), compare);
    int roop_count = keymap.back().length(); // target중 제일 긴문자를 roop횟수로 설정
	
	//answer를 target벡터 갯수만큼 초기화
    vector<int> answer;
    for (int i = 0; i < targets.size(); i++)
    {
        answer.push_back(0);
    }
    bool fail_check = false;

    for (int a = 0; a < targets.size(); a++)
    {
        //1.비교할 target 선정
        string target_str = targets[a];// targets 문자열 정의
        int target_length = target_str.length();

        for (int b = 0; b < target_length; b++)
        {
            //1-1.target Char 설정
            char target_char = target_str.at(b);

            //2.target_Char vs keymap_list (비교)
            //2-1.keymap_char_index 설정( 리스트에 공통)
            
            //c : target_char_index
            for (int c = 0; c < roop_count; c++)
            {
                //2-2.keymap_list_index 루프 시작
                if (char_compare(target_char, keymap, c))
                { 
                    answer[a] += c + 1;
                    break;
                }
                
          	   //해당되는 글자를 못찾을경우 -1과 bool값 변경
                if (c == roop_count - 1)
                {
                    answer[a] = -1;
                    fail_check = true;
                }
            }
			
			//해당 문자열은 출력 못하므로 문자열 반복문 탈출 
            if (fail_check)
            {
                fail_check = false;
                break;
            }

        }
    }

    return  answer;
}

<다른사람의 풀이>

더보기

이 풀이는 target을 먼저 탐색하기 전에 , keymap을 루프를 돌려 해당 문자를 출력하기 위한 최소의 횟수를 미리 리스트화 한뒤 target에 접근 하는 방식이다.

플로우차트는

1.26개(알파벳갯수)의 사이즈의 벡터를 100000(MARKER)로 전부 초기화

1-1.keymap을 기준으로 루프를 돌려 table에 해당 문자열을 찾기위해 최소 횟수 탐색
ex) 

target = { ABACD , BECFD }

첫번째 루프 (ABACD)

table[0] = 1 // 1=min(100000 , 1) 알파벳 A

table[1] = 2 // 1=min(100000 , 2) 알파벳 B

table[0] = 1 // 1=min(1 , 1) 알파벳 A

table[2] = 4 // 4=min(100000 , 4) 알파벳 C

table[3] = 5 // 5=min(100000 , 5) 알파벳 D

 

두번째 루프 (BCEFD)

table[1] = 1 // 1=min(2 , 1) 알파벳 B

table[2] = 2 // 2=min(4 , 2) 알파벳 C

table[4] = 3 // 1=min(100000 , 3) 알파벳 E

table[5] = 4 // 4=min(100000 , 4) 알파벳 F

table[3] = 5 // 5=min(5 , 5) 알파벳 D

 

최종 결과물
table[0] = 1  // 알파벳 A를 출력하기 위한 최소 횟수

table[1] = 1 // 알파벳 B를 출력하기 위한 최소 횟수

table[2] = 2 // 알파벳 C를 출력하기 위한 최소 횟수

table[3] = 5 // 알파벳 D를 출력하기 위한 최소 횟수

table[4] = 3 // 알파벳 E를 출력하기 위한 최소 횟수

table[5] = 4 // 알파벳 F를 출력하기 위한 최소 횟수

table[6] = 100000 // 알파벳 G를 출력하지 못함
... 

 

 이런식의 roop를 통해 미리 알파벳의 최소 루트를 구해놓은뒤 작업을 한다 .

#include <string>
#include <vector>

using namespace std;

vector<int> solution(vector<string> keymap, vector<string> targets) {
    vector<int> answer;

    const int MARKER = 1000000; //  MARKER : 사용하지않는것을 표시
    vector<int> table(26, MARKER);  //26개(알파벳 갯수)의 벡터 사이즈 설정 , MARKER(100000)으로 초기화
   
   //keymap를 루프 둘려 각 단일문자를 최소의 횟수로 찾아가는지 탐색 
    for (const auto& v : keymap)
    {
        for (int i = 0; i < v.size(); ++i)
        {
            table[v[i] - 'A'] = min(table[v[i] - 'A'], i + 1);
        }
    }

    for (const auto& str : targets)
    {
        int total = 0;
        for (const auto c : str)
        {
            //MARKER와 같으면 이 문자는 출력할수 없다는 뜻
            if (table[c - 'A'] == MARKER)
            {
                //결과값 -1과 해당 문자열 탈출 
                total = -1;
                break;
            }
            else
            {
				//위에서 구한 해당 문자의 최소 출력횟수를 total에 더함
                total += table[c - 'A'];
            }
        }
		
        //push_back과 같은 의미지만 자료형이 같을경우 사용할수 있는 push_back
        answer.emplace_back(total);
    }

    return answer;
}

 

 

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV.2 최솟값 만들기  (0) 2023.06.09
LV.1 내적  (0) 2023.06.09
LV.1 없는 숫자 더하기  (0) 2023.06.09
LV.1 공원산책  (0) 2023.06.08
[LV2] 귤 고르기  (0) 2023.05.31

+ Recent posts