1. 문제

1) 문제 링크

    : https://www.acmicpc.net/problem/2110

 

2110번: 공유기 설치

첫째 줄에 집의 개수 N (2 ≤ N ≤ 200,000)과 공유기의 개수 C (2 ≤ C ≤ N)이 하나 이상의 빈 칸을 사이에 두고 주어진다. 둘째 줄부터 N개의 줄에는 집의 좌표를 나타내는 xi (0 ≤ xi ≤ 1,000,000,000)가

www.acmicpc.net

 

2) 설명

수평선 위에 N개의 집이 있고 N개의 집에 C개의 공유기를 설치하는 문제.

문제에서 말하는 "가장 인접한 두 공유기 사이의 거리를 최대로 하는" 이 조금 이해하기 힘들었지만 결국

공유기가 설치된 집들 간의 거리의 최소값을 최대로 만들면 된다.(말이 조금 어렵다)

예를 들어

index 0 1 2 3 4 5
위치 1 3 8 10 14 20
공유기 설치 O X X O X O

위와 같은 상황이라면 답은 0번째 집, 3번째 집, 5번째 집에 설치를 해서 공유기가 설치된 이웃집 간 최소 거리의 최대값인 9 ( maximize( min( dist(house[0], house[3]), dist(house[3], house[5]) ) ) ) 이 된다.

 

2. 풀이

1) 알고리즘

    : 이분탐색

 

2) 풀이 설명

 공유기가 설치된 이웃집 거리는 1에서 (수평선에서 가장 오른쪽 집과 가장 왼쪽 집 거리) 범위 안에 있을 수 있다.

그래서 그 거리를 구하기 위해 + 이분 탐색을 위해 houses(집 위치 벡터)를 정렬해준다.

이후 이분탐색을 통해서 최소 거리의 최대값을 찾아나간다.

이때 getMinDist 함수를 통해서 인자로 입력받은 거리 이상으로 공유기 설치가 가능한지 판단하고 불가능하다면 -1을, 가능하다면 그때의 거리 최소값(인자로 입력받은 거리 이상의)을 리턴해주도록한다.

 

getMinDist 함수에서 첫 번째 집(가장 왼쪽)은 반드시 공유기를 설치한다고 가정한다. 그 이유는 다음과 같다.

처음 공유기가 설치되는 집이 x번째 ( x > 1 )라고 해보자. 그러면 x번쨰 집(hosues[x])과 그 다음 공유기가 설치된 집(houses[x + n]) 간의 거리는

1. 공유기가 설치된 집 중 가장 가까운 거리

2. 공유기가 설치된 집 중 가장 가깝지는 않은 거리

중 하나다.

당연한 말 같아보이지만 첫 번째 집에 공유기를 설치해야되는 이유를 설명하는 데 이 두 가지 경우를 나눠서 생각하는 게 필요하다고 생각한다.

이제 x번째 집에서 공유기를 제거하고 첫 번째 집(houses[0])에 공유기를 설치한다고 생각해보자.

그러면 그 다음 공유기가 설치된 집(houses[x + n])과 첫 번째 공유기가 설치된 집(이제는 houses[0]) 사이의 거리는 공유기를 x번째에 설치했을 때보다 멀어질 것이다.

그러면 위의 1번의 경우에는 공유기가 설치된 집 간 거리 중 최소값이 증가하게 된 것이니까 답을 잘못 구한 게 된다.(더 큰 최소값을 찾을 수 있다는 말)

그리고 2번의 경우에는 이 첫 번째 공유기가 설치된 집과 두 번째 공유기가 설치된 집 사이의 거리가 늘어나도 어차피 최소 거리보다는 크기 때문에 전혀 상관이 없다.

결론적으로 문제에서 요구하는 "가장 인접한 두 공유기 사이의 거리를 최대로 하는 집들"을 선택했을 때 그 시작이 첫 번째(houses[0])집이 아니라면 1번의 경우에는 더 큰 최소값이 있으므로 답을 잘못 구한 게 되고 2번의 경우에는 첫 번째 집을 고르든 말든 전혀 상관이 없다는 말이다. 즉 첫 번째 집에 무조건 공유기를 설치했다고 가정하는 것은 답을 구하는 데 충분조건으로 작용한다는 말이다.

이 논리는 반대로 마지막 집에도 똑같이 적용된다.

 

또한 사실 getMinDist 함수를 인자로 받은 거리로 공유기 설치가 가능한지만 판단해줘도 되는데 모든 집 사이의 거리가 넓은 경우에 시간을 더 단축할 수 있지 않을까 해서 인자로 받은 거리 이상의 거리(즉 공유기가 설치된 집 간의 거리가 인자로 받은 거리 이상으로 위치하게 하는 실제 거리)를 리턴하도록 해주었다. (근데 속도를 보니 크게 시간 단축의 효과는 없는 것 같았다ㅠㅠ)

 

3. 코드

더보기

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef long long LL;

const LL MAX_VAL = 999999999;
LL N, C;
vector<LL> houses;

LL getMinDist(LL minDist){
	LL answer = MAX_VAL;
	int prevHouse = 0; // 이전 집과의 거리를 구하기 위해 이전 집의 index를 저장해놓을 변수
	LL numOfAP = 1; // 설치된 공유기의 개수. 첫 집에 설치된 상태에서 시작하기 때문에 초기값 1

	for(int i=1;i<houses.size() && numOfAP<=C;i++){
		if(houses[i] - houses[prevHouse] >= minDist){
        	//집 간 거리가 인자로 받은 최소거리보다 크다면 업데이트
			answer = min(answer, houses[i] - houses[prevHouse]); // 거리 업데이트
			prevHouse = i; // 이전 집 index 업데이트
			numOfAP++; // 설치된 공유기 개수 증가
		}
	}

	if(numOfAP < C){
		//공유기를 적게 놓았으면 -1 return
		return -1;
	} else {
		return answer;
	}
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	LL input, answer = 0;
	cin >> N >> C;
	
	houses.reserve(N);
	for(int i=0;i<N;i++){
		cin >> input;
		houses.push_back(input);
	}
	
	//sort
	sort(houses.begin(), houses.end());
	
	//binary search
	LL s = 0;
	LL e = houses.back() - houses.front();
	while(s<=e){
		LL mid = (s + e)/2;
		LL minDist = getMinDist(mid);//mid 이상의 거리의 최소값을 구함

		if(minDist >= mid){
			s = minDist + 1;
			answer = max(answer, minDist);// 최소 거리의 최대값을 구해야 하므로 minDist >= mid 일 때만 업데이트
		} else {
			e = mid - 1;
		}
	}
	
	cout << answer << endl;

	return 0;
}

 

 

4. 느낀점

이분탐색은 자주 구현해도 어려운 것 같다...

'알고리즘 > 백준' 카테고리의 다른 글

[백준/7512] 연속하는 소수의 합  (0) 2021.09.20
[백준/1727] 커플 맞추기  (0) 2021.08.25
[BOJ/1034] 램  (0) 2021.07.27
[BOJ/7579] 앱  (0) 2020.12.29
[BOJ/14891] 톱니바퀴  (0) 2020.12.18

나는 비전공자(경제학과)에서 네카라 개발자로 취업에 성공한 삐리리뽀옹이다.

 

삐리리가 취업하기까지 많은 일들이 있었는데, 그 경험들을 기록해두지 않으면 나중에 까먹을 것 같아서 블로그에 적어본다.

 

일단 스펙은 다음과 같다.

 

1. 서울 중위권 대학 경제학전공, 컴퓨터공학 복수전공

2. 총 평점 3.84

3. 경제학과 스펙

  - 전공 평점 3.91

  - 교내 학술제 최우수상

  - 한국은행 통화정책경시대회 예선 탈락

  - 매경TEST 우수

4. 컴퓨터공학과 스펙

  - 전공 평점 3.84

  - 졸업전시회 대상

  - 정보처리학회 추계학술대회 동상

  - 연구실 활동 6개월

  - 소프트웨어공학 조교

  - 정보처리기사

  - SQLD

5. 공통 스펙

  - 토익스피킹 lv6

  - 컴활2급

  - 워드 1급

  - 음악 동아리 5년

  - 수학 과외 5년

 

나는 컴퓨터에 관련해서는 엑셀만 좀 쓸 줄 아는 일자무식 비전공자에서 개발자의 커리큘럼?을 쌓기 위해 꽤 오랜 시간(학부 3년 반, 취준 1년 반)을 들였다.

25살 하반기부터 시작해서 29살 직전까지 컴퓨터공학과 정규 커리큘럼을 처음부터 그대로 따라갔으니까 사실 취업 당시에는 비전공자라고 말하기 조금 그럴 수도 있다..

이건 나름 비전공자는 실력이 떨어진다는 약점을 제거하고 싶어서 늦더라도 정석 루트를 따라갔던 거였는데 그만한 실력이 쌓였나?는 잘 모르겠다.. (그냥 덜 놀고 처음부터 빡세게 해서 취업해서 더 배우면 좋았을 수도 있겠다는 생각도 든다ㅠㅠ)

 

공부한 내용이나 공부 방법같은 건 나중에 기회되면 적어보기로 하고 일단은 시간 날 때마다 취업 준비 과정에서 겪었던 나름 힘들었던 혹은 보람찼던 경험들을 기록해두고자 한다..

1. 문제

1) 문제 링크

    : https://programmers.co.kr/learn/courses/30/lessons/81302

 

코딩테스트 연습 - 거리두기 확인하기

[["POOOP", "OXXOX", "OPXPX", "OOXOX", "POXXP"], ["POOPX", "OXPXP", "PXXXO", "OXXXO", "OOOPP"], ["PXOPX", "OXOXP", "OXPOX", "OXXOP", "PXPOX"], ["OOOXX", "XOOOX", "OOOXX", "OXOOX", "OOOOO"], ["PXPXP", "XPXPX", "PXPXP", "XPXPX", "PXPXP"]] [1, 0, 1, 1, 1]

programmers.co.kr

 

2) 설명

면접장(5 by 5)에서 면접자들이 맨해튼거리(가로 길이 + 세로 길이) 기준 2 이상 떨어져 앉아있을 때 거리두기를 지킨 것.

맨해튼거리가 2이하여도 그 사이에 파티션이 있다면 거리두기를 지킨 거다

places(면접장 배열)이 주어지면 각 place(면접장)이 거리두기를 지켰는지(1) 안 지켰는지(0)를 배열로 리턴하는 문제

참고  

- 면접자 : P

- 파티션 : X

- 빈자리 : O

2. 풀이

1) 알고리즘

    : 시뮬레이션(BFS)

 

2) 풀이 설명

- 첫 시도!

BFS를 사용해 풀이를 시도했다.

isFar 함수를 통해 각 place(면접장)이 거리두기가 지켜졌는지 확인한다.

P의 좌표들을 모두 큐에 삽입한다

큐가 빌 때까지 c(거리)가 2 이하이고 해당 P에서 방문한 적이 없는(visited[nx][ny]가 v(탐색 시작한 P의 idx)와 다른) 좌표들의 visited값을 v로 업데이트해준다

다음의 경우 visited[2, 2] 에서 1, 2가 다르기 때문에 그냥 2로 업데이트해주면 되는 거다

    visited 배열    
1 1 1   2
1 P(idx=1) 1 2 2
1 X 1 2 2 P(idx=2)
X X   2 2
P(idx=3) 3 3   2

 

만약 다음과 같이 P를 발견하면 return false 해준다

    visited 배열    
1 1 1   2
1 P(idx=1) 1 2 2
1 X 2 2 P(idx=2)
X X   2 2
        P(idx=3)

 

 

사실 P가 있냐 없냐만 보면 되는데 왜 visited 배열을 썼는가 하면 같은 자리를 두 번 이상 방문하지 않도록 하기 위해서다. 예를 들어 [0, 0]은 P(idx=1)이 좌 -> 상 이동, 상 -> 좌 이동 두 번 방문해야하는데 이때 visited배열을 보고 visited[0][0](=1) 과 v(=1)이 같기 때문에 더 방문하지 않도록 하는 것이다.

 

풀고나서 4개짜리 tuple을 썼다는 게 마음에 안 들었는데 찾아보니 더 간단한 방법이 있었다!

 

- 두 번째 시도!

사실 두 번째 풀이의 핵심은 한 면접자당 BFS를 1 depth까지만 탐색하는 거였다.

i번째 면접자가 방문한 자리를 다른 면접자가 방문한다면 이건 올바르지 않은 거리두기를 하게 된 것이다

설명하자면 교점을 기준으로 보았을 때 [x, y] 교점으로부터 거리가 1인 면접자가 둘 이상 있다면 그 면접자들 간의 거리는 2 이하가 되기 때문이다

예를 들어 다음과 같은 경우에 [4, 3]자리를 P(idx=2)가 방문했는데 P(idx=3)이 또 방문한다면 이 둘은 거리가 2 이하기 때문에 return false 해주면 된다

 

    visited 배열    
  1      
1 P(idx=1) 1   2
  X   2 P(idx=2)
        2 3
        P(idx=3)

 

 

3. 코드

더보기

첫 번째 방법

//BFS 탐색을 2 depth까지 하는 비효율적인 방식의 첫 시도
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <tuple>

using namespace std;

bool isFar(const int boardSize, vector<string> place, vector<vector<int>> &visited){
    const vector<int> dx = {-1, 1, 0, 0};
    const vector<int> dy = {0, 0, -1, 1};
    
    queue<tuple<int, int, int, int>> q;
        
    for(int i=0;i<boardSize;i++){
        for(int j=0;j<boardSize;j++){
            if(place[i][j] == 'P')
				q.push({i, j, 0, q.size()});
        }
    }
	while(!q.empty()){
      	int x = get<0>(q.front());
        int y = get<1>(q.front());
        int c = get<2>(q.front()); // 거리
        int v = get<3>(q.front()); // 탐색 중인 면접자의 index
        q.pop();
        
        visited[x][y] = v;
        
        for(int d=0;d<4;d++){
            int nx = x + dx[d];
            int ny = y + dy[d];
            int nc = c + 1;
                
            if(nx<0 || nx>=boardSize || ny<0 || ny>=boardSize
            	|| visited[nx][ny]==visited[x][y] // 탐색 중인 면접자가 방문한 적이 있다면 continue
               	|| place[nx][ny]=='X' // 파티션이면 continue
              	|| nc>2) // 다음에 갈 자리가 맨해튼 거리 2 초과면 continue
            	continue;
            
            if(place[nx][ny] == 'P')
                return false;
            q.push({nx, ny, nc, v});
        }
    }
    
    return true;
}

vector<int> solution(vector<vector<string>> places) {
	vector<int> answer;
    const int boardSize = 5;
	vector<vector<int>> visited(boardSize, vector<int>(boardSize, -1));
	
    for(auto place : places)
		answer.push_back(isFar(boardSize, place, visited));
    
	return answer;
}

 

두 번째 방법

//1 depth BFS 탐색을 통한 효율적인 방법
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <tuple>

using namespace std;

bool isFar(const int boardSize, vector<string> place){
    const vector<int> di = {-1, 1, 0, 0};
    const vector<int> dj = {0, 0, -1, 1};
    
    vector<vector<bool>> visited(boardSize, vector<bool>(boardSize, false));
        
    for(int i=0;i<boardSize;i++){
        for(int j=0;j<boardSize;j++){
            if(place[i][j] == 'P'){
                visited[i][j] = true;
            	for(int d=0;d<4;d++){
                    int ni = i + di[d];
            		int nj = j + dj[d];
                    if(ni<0 || ni>=boardSize || nj<0 || nj>=boardSize || place[ni][nj]=='X')
                        continue;
                    if(visited[ni][nj])
                        return false;
                    visited[ni][nj] = true;
                }    
            }
        }
    }
    
    return true;
}

vector<int> solution(vector<vector<string>> places) {
	vector<int> answer;
    const int boardSize = 5;
	
    for(auto place : places)
		answer.push_back(isFar(boardSize, place));
    
	return answer;
}

4. 느낀점

내 생각에는

엄청 길지는 않은 시간에

엄청 느리지는 않은 알고리즘으로

엄청 가독성 떨어지지는 않는 코드로

성공은 했지만 다 최선의 방법은 아니었다ㅠㅠ

면접자가 중심이라 생각해서 면접자를 기준으로만 생각했는데 교점을 기준으로 생각했으면 좋았을 텐데 아쉬웠다...

보이지 않는 걸 보는 연습이 필요한 것 같다..!

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[LV2] 뉴스 클러스터링  (0) 2021.08.26
[LV4] 4단 고음  (0) 2021.08.19
[LV3] 불량 사용자  (0) 2021.08.13
[LV4] 호텔 방 배정  (0) 2021.08.13

1. 문제

1) 문제 링크

    : https://www.acmicpc.net/problem/1034

 

1034번: 램프

첫째 줄에 N과 M이 주어진다. N은 행의 개수이고, M은 열의 개수이다. N과 M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 램프의 상태가 주어진다. 1이 켜져있는 상태이고, 0이 꺼져

www.acmicpc.net

 

2) 설명

램프의 열을 조작하는 스위치를 K번 조작해서 램프가 모두 켜져 있는 행의 개수를 최대로 만드는 문제

2. 풀이

1) 알고리즘

    : 브루트포스

 

2) 풀이 설명

   2-1) 재귀

    첫 시도는 재귀로 시도했으나 시간초과로 실패했다. 생각해보니 열이 최대 50개인데 50개의 열을 켜고 끄는 경우를 생각하면 2의 50제곱이 되어서 당연히 시간초과가 날 수밖에 없던 것 같다.

    그래서 해당 열까지 모든 램프가 켜진 행의 개수가 이전에 구했던 모든 램프가 켜진 행의 최대 개수보다 작거나 같다면 더 탐색을 진행하지 않는다. (왜냐하면 더 탐색을 해봤자 이전에 구한 최대값을 넘을 수 없기 때문에)

    이렇게 하니까 일단 답은 맞았다.

 

  2-2) 맵 사용

    다른 사람들의 풀이를 참고해보니 초기 램프 배열에서 패턴이 똑같은 행의 개수를 구하는 게 더 편해보였다.

    대신 이때 초기에 한 행에서 꺼져 있는 램프의 개수가 K개 이하여야 하고, 남은 스위치 조작 횟수(K - 꺼진 램프 개수)가 짝수 개여야 한다. 스위치를 조작해야될 횟수가 x번 남았을 때 x가 짝수라면 아무 열이나 껐다 켰다 두 번씩 반복하면 되는데, x가 홀수 번이라면 껐다 켰다를 반복하고나서 마지막에 결국 한 번 더 뒤집어야 하기 때문이다.(결국 램프가 다 켜진 행에서 한 번 더 스위치를 바꾸므로 한 개의 램프가 꺼지게 된다)

 

    그래서 맵(key : 램프 행, value : 개수)을 사용해서 초기 램프 행들 중 같은 패턴을 가지는 행 개수를 카운팅 해줬다.

 

    예를 들어

 

    1 - 1001

    2 - 0100

    3 - 1100

    4 - 1001

 

    이렇게 있다면 맵에는 {1001 : 2, 0100 : 1, 1100 : 1} 이렇게 들어가게 된다. 그리고 이 맵을 돌면서 행에서 꺼진 램프의 개수가 K개 이하이고, 꺼진 램프를 모두 켰을 때 (K - 꺼진 램프의 개수)가 짝수인 경우에 답을 업데이트해나갔다.

 

3. 코드

더보기
//재귀 풀이

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

using namespace std;

int N, M, K, answer = -1;
vector<string> lamp(50);

void reverseCol(int colIdx){
	//colIdx 열 램프 뒤집기
	for(int i=0;i<N;i++)
		lamp[i][colIdx] = (lamp[i][colIdx] == '0'? '1' : '0');
}

int countRow(int colIdx){
	//colIdx까지 몇 개의 램프가 켜져있는지 확인
	if(colIdx == 0)
		return 0;
	int i, j, answer = 0;
	for(i=0;i<N;i++){
		for(j=0;j<colIdx;j++){
			if(lamp[i][j] == '0')
				break;
		}
		if(j == colIdx)
			answer++;
	}
	return answer;
}

/*
	재귀 접근
	@colIdx 열 번호
	@changeCount 램프 열을 켜고 끌 수 있는 남은 횟수
*/
void solve(int colIdx, int changeCount){
	if(colIdx == M){
		//마지막 열일 경우
		if((K - changeCount)%2 == 0)
			//램프를 켜고 끌 수 있는 남은 횟수가 짝수일 경우에만 업데이트
			answer = max(answer, countRow(M));
		return;
	}
	
	//앞서 구한 최대로 켤 수 있는 램프 행의 수보다 켜져있는 램프의 수가 적으면 더 돌리지 않음
	if(countRow(colIdx + 1) > answer)
		solve(colIdx + 1, changeCount);

	reverseCol(colIdx);//뒤집기
	if(countRow(colIdx + 1) > answer && changeCount < K)
		//앞서 구한 최대로 켤 수 있는 램프 행의 수보다 켜져있는 램프 행의 수가 적으면 더 돌리지 않음
		solve(colIdx + 1, changeCount + 1);
	reverseCol(colIdx);//다시 뒤집기
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> N >> M;
	for(int i=0;i<N;i++)
		cin >> lamp[i];
	cin >> K;
	
	solve(0, 0);

	cout << answer << endl;

	return 0;
}
//패턴과 맵을 이용한 풀이
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int N, M, K, answer = 0;
	cin >> N >> M;
	string lamp;
	//램프 행, 개수
	map<string, int> lampMap;

	for(int i=0;i<N;i++){
		cin >> lamp;
		//램프 맵에 카운트 증가
		lampMap[lamp] += 1;
	}
	cin >> K;

	for(auto x : lampMap){
		string row = x.first;
		int val	= x.second;
		//행에 있는 0의 개수
		int zeroCnt = count(row.begin(), row.end(), '0');
		
		//행에 있는 0을 모두 켰을 때 램프 열을 뒤집을 남은 횟수가 짝수여야 함.
		if(zeroCnt <= K && (K - zeroCnt)%2 == 0)
			answer = max(answer, val);
	}

	cout << answer << endl;

	return 0;
}

 

4. 느낀점

- 재귀에서 시간초과난다고 포기하지 않은 나 자신 칭찬한다

- 하지만 아집을 버리고 포기했더라면 더 좋은 방법을 찾을 수도 있었을 텐데 미한한 나 자신 혼낸다

- 사실 스트링 패턴으로 접근하는 풀이는 나 혼자서는 오래 고민한다 해도 생각해내기 힘들었을 것 같다. 좀 더 열린, 다른 시각으로 접근하는 연습을 해보도록 하자

'알고리즘 > 백준' 카테고리의 다른 글

[백준/1727] 커플 맞추기  (0) 2021.08.25
[백준/2110] 공유기 설치  (0) 2021.08.01
[BOJ/7579] 앱  (0) 2020.12.29
[BOJ/14891] 톱니바퀴  (0) 2020.12.18
[백준/9656] 돌 게임 2  (0) 2020.11.02

1. HTTP

1)  HTTP란?

    A) 웹상에서 클라이언트와 서버 간 통신을 위한 프로토콜

    B) Application 계층

    C) HTTP가 신뢰성을 보장해주기 때문에 이론적으로는 TCP(Transmission Control Protocol), UDP(User Datagram Protocol) 중 아무거나 사용 가능

2) 설명

 

2. HTTP/0.9

1) GET method만 있음

2) HTML 리소스만 처리 가능

 

3. HTTP/1.0

1) 0.9의 확장

2) Header 사용 -> version, status code,  Content-Type -> HTML 이외의 다른 파일도 가능

3) 1 Connection : 1 request + 1 reply -> 매번 새로운 연결로 성능 저하 + 서버 부하 비용 증가

 

4. HTTP/1.1

1) 1.0의 확장

2) Persistent Connection : 지정한 timeout 동안 커넥션을 닫지 않는 방식

3) 파이프라이닝 기법 도입 : 하나의 커넥션에서 응답을 기다리지 않고 순차적인 여러 요청을 연속적으로 보내 그 순서에 맞춰 응답을 받는 방 -> Head Of Line Blocking 문제 발생!

4) 연속된 request, reply일 경우에는 Header가 중복됨에도 계속 Header를 표기함으로써 리소스가 낭비됨

 

5. HTTP/2.0

1) 1.1의 확장(2015~6 에 등장)

2) 메시지 전송 방식의 변화

   A) 바이너리 프레이밍 계층 사용 : 파싱, 전송 속도 향상, 오류 발생 가능성 저하

   B) Connection 안의 스트림(데이터 양방향 전송)에 프레임 단위로 들어가 합쳐짐

   C) request, reply의 Multiplexing이 가능(인터리빙 방식) -> HOL 문제를 해결

   D) Stream Prioritization :리소스 간 우선 순위 설정 가능

   E) Server Push : 클라이언트가 요청하지 않아도 서버가 알아서 보내줌

       ex) client가 HTML 리소스를 요청하면 CSS, JS도 요청하겠구나 하고 같이 보내버림

   F) Header Compression :Static Dynamic Table 개념 도입 + 중복되지 않은 데이터를 허프만 인코딩 해서 압축 -> 헤더의 크기(약 85% 정도 줄어듦)를 줄여 페이지 로드 시간 감소 

6. QUIC

1) 구글에서 만든 전송 계층 프로토콜(현재 구글 관련 제품 대부분의 기본 프로토콜)

2) 2013년에 공개

3) UDP 기반 -> 왜? TCP의 헤더 길이나 복잡한 알고리즘으로 인한 latency를 방지하기 위해서 + UDP는 데이터 전송에 집중한 설계이고 별도 기능 없이 원하는 기능만 구현할 수 있기 때문에

4) 전송 속도 향 : 첫 연결 설정에서 필요한 정보와 함께 데이터를 전송 -> 연결 성공 시 설정을 캐싱하여 다음 연결 때 3 way handshake 없이 바로 성립 가능(Connection UUID 를 사용)

5) TLS 기본 적용, IP Spoofing / Replay Attack 방지 -> 보안성 향상

6) 독립 스트림 사용으로 향상된 Multiplexing 가능(HTTP/2.0에서도 Multiplexing을 지원해주지만 TCP 자체에서 일어나는 HOL을 개선)

7) 구글의 경우 로딩 시간 평균 3% 개선, 유투브 시청 중 버퍼링 평균 30% 개선

 

7. HTTP/3.0

1) 2018년 11월에 이미 QUIC을 기반으로 등장 -> 구글은 이미 사용 중

 

8. 느낀점

급하게 정리한 내용이라 추후 다시 정리 예정

1. accumulate 함수

1) 특정 구간에 있는 배열의 원소들을 특정 방식에 맞게 쌓아서 리턴해주는 함수

 - 기본적으로 합계 구하는 데 많이 쓴다)

 

2) numeric 헤더에 있다

 

3) accumulate([배열 시작 주소], [배열 끝 주소], [초기값], [연산 함수])

 - 배열 시작 주소, 배열 끝 주소, 초기값은 반드시 입력해야 한다

 - 연산 함수를 따로 지정하지 않으면 + 연산으로 수행이 되고, 다른 연산자를 지정해줄 수도 있다.(예를 들어 곱셈은 multiplies<int>() 를 넣어주면 된다.)

 4) 사용자 지정 함수를 사용하는 건 조금 복잡하다

 - 예를 들어 3, 2, 5, 10, 2 원소가 있을 때 3 <-> 2 <-> 5 <-> 10 <-> 2 의 문자열 결과를 출력하고 싶다면 배열의 원소들을 저런 방식으로 accumulate 해줄 수 있는 사용자 지정 함수를 사용해야 한다.

 - 작동 방식은 다음과 같다

  (A) "3"

  (B) "3 <-> 2"

  (C) "3 <-> 2 <-> 5"

   ...

  (N) "3 <-> 2 <-> 5 <-> 10 <-> 2"

 - 이때 배열 원소 타입이 int이기 때문에 string으로 바꿔줘야(to_string) 하는 점을 고려해야 한다. 

 - 근데 이럴 경우 리턴 값이 string이 되기 때문에 이 리턴값을 계속 accumulate 해가려면 함수의 인자도 string, int 여야 한다. 그러므로 [초기값]을 "3"으로 설정하고, [배열 시작 주소]를 배열 시작 주소의 다음 주소(두 번째 원소 : int형)로 해줘야 함수 인자인 (string, int) 형식을 맞춰줄 수 있다.

 - 만약 배열 시작 주소를 두 번째 원소의 주소[next(arr.begin())] 으로 안 하고 배열 시작 주소[ arr. begin() ] 으로 해버린다면 "3 <-> 3 <-> 2 <-> 5 <-> 10 <-> 2" 값이 나오게 될 것이다.

 

4) 예시

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>

using namespace std;

int main()
{
	vector<int> arr = {3, 2, 5, 10, 2};
	
	//합계
	cout << "SUM : " << accumulate(arr.begin(), arr.end(), 0) << endl;

	//곱셈
	cout << "MULT : " << accumulate(arr.begin(), arr.end(), 1, multiplies<int>()) << endl;
	
	//사용자 지정 함수
	auto myOperation = [](string a, int b){
		return a + " <-> " + to_string(b);
	};
	cout << "My funct : " << accumulate(next(arr.begin()), arr.end(), to_string(arr[0]), myOperation) << endl;
    
	return 0;
}

 

 

'프로그래밍 언어 > C++' 카테고리의 다른 글

개발환경 세팅  (1) 2024.01.14
코드가 돌아가는 원리  (1) 2023.12.28
프로그래밍과 C++  (0) 2023.12.27

1. 문제

1) 문제 링크

    : www.acmicpc.net/problem/7579

 

7579번: 앱

입력은 3줄로 이루어져 있다. 첫 줄에는 정수 N과 M이 공백문자로 구분되어 주어지며, 둘째 줄과 셋째 줄에는 각각 N개의 정수가 공백문자로 구분되어 주어진다. 둘째 줄의 N개의 정수는 현재 활

www.acmicpc.net

 

2) 설명

 각각의 프로세스가 메모리를 m_i만큼 사용하고 있고, 각 프로세스를 비활성화시킬 때 드는 비용이 c_i라면, 최소 M만큼의 메모리 공간을 확보하는 데 필요한 최소 비용을 구하는 문제

2. 풀이

1) 알고리즘

    : DP(배낭)

 

2) 풀이 설명

 A) 첫 시도 : 2차원 배열 사용하기

    - 처음에는 메모리 생각을 안 하고 100(N 최댓값) x 10000000(M 최댓값) 2차원 배열을 사용했다.

     메모리 초과가 나서 안 되니까 map을 사용해보자는 생각으로 map을 사용했지만 역시나 메모리 초과로 실패했다.     (어차피 사용량 많아지면 비슷해지는 것 같다)

 

 B) 두번째 시도 : 1차원 배열 사용하기

     - 한참 고민하다가 결국 다른 사람들 풀이를 참고해서 풀게 되었다.

      dp 배열 : 1차원 배열로 dp[cost] : cost를 사용해서 최대로 확보할 수 있는 memory

     이제 이중 반복문을 통해 dp 배열을 업데이트해나갔다.

     점화식 : dp[j] = max(dp[j], dp[j - C[i]] + A[i])

      i번째 프로세스를 비활성화할 경우 dp 배열을 업데이트 해나가는 점화식이다.

      만약 활성화된 채로 두는 게 더 큰 memory를 확보할 수 있다면 dp[j] 가 선택 될 것이고, 비활성화하는 게 더 큰 메모리를 확보할 수 있다면 dp[j-C[i]] + A[i] 가 선택될 것이다.

 

3. 코드

더보기
/*
	앱
	DP
    1차원 배열을 사용한 풀이
*/
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

#define N_MAX 100
#define C_MAX 100
#define M_MAX 10000000

using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int N, M, maxCost, answer;
	vector<int> A(N_MAX, 0), C(N_MAX, 0);
	vector<int> dp(N_MAX*C_MAX + 1, 0);

	cin >> N >> M;
	
	for(int i=0;i<N;i++) cin >> A[i];
	for(int i=0;i<N;i++) cin >> C[i];
	
	maxCost = accumulate(C.begin(), C.end(), 0);
	
	for(int i=0;i<N;i++)
		for(int j=maxCost;j>=C[i];j--)
			dp[j] = max(dp[j], dp[j - C[i]] + A[i]);

	for(answer=0;answer<maxCost && dp[answer]<M;answer++);
	cout << answer << "\n";
}

 

/*
	앱
	DP
    vector와 map을 중첩하여 사용한 풀이
*/
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>

#define N_MAX 100
#define C_MAX 100
#define M_MAX 10000000

using namespace std;

int N, M;
vector<int> A(N_MAX, 0), C(N_MAX, 0);
vector<map<int, int>> dp(N_MAX);

int solve(int idx, int needMemory){
	if(idx >= N)
		return M_MAX;	
	if(needMemory <= 0)
		return 0;

	if(dp[idx][needMemory] <= 0)
		dp[idx][needMemory] = min(solve(idx + 1, needMemory), C[idx] + solve(idx + 1, needMemory - A[idx]));

	return dp[idx][needMemory];
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> N >> M;
	
	for(int i=0;i<N;i++) cin >> A[i];
	for(int i=0;i<N;i++) cin >> C[i];

	cout << solve(0, M) << endl;
}

 

4. 느낀점

1) 좀 더 다방면의 방법을 생각해야겠다. 너무 N, M 값에만 집중해서 생각하다보니 다른 방법을 잘 못 찾았다.

'알고리즘 > 백준' 카테고리의 다른 글

[백준/2110] 공유기 설치  (0) 2021.08.01
[BOJ/1034] 램  (0) 2021.07.27
[BOJ/14891] 톱니바퀴  (0) 2020.12.18
[백준/9656] 돌 게임 2  (0) 2020.11.02
[백준/2631] 줄세우기  (0) 2020.11.02

1. "Could not resolve host: gtihub.com" Error

 1) 상황

      A) 깃허브에 push 하려다 발생 -> 다른 임시 repository 생성 후 clone, pull 등 다 안 됨

 

 2) 원인 및 해결

      A) 프록시 서버 설정 오류

            - 프록시 설정을 바꿈으로써 해결 가능하다고 한다. 근데 내 문제는 이게 아니었다.

      B) ssl vpn 접속.

            - ssl vpn 접속해서 회사 업무 수행 도중에 깃헙을 사용하려고 한 게 문제였다. ssl vpn 접속 끄고 하면 잘 됨!

 

 

+ Recent posts