나만의 문제집 ( 쉽게, 깔끔하게, 시간복잡도가 좋게 )

Date:     Updated:

카테고리:

Chapter 01 자료구조 ( 중요도 상 )

01-1 전화번호 목록 ( 프로그래머스 고득점 Kit, 해시 )

  • 전화번호부에 적힌 전화번호를 담은 배열 phone_book 이 solution 함수의 매개변수로 주어질 때, 어떤 번호가 다른 번호의 접두어인 경우가 있으면 false를 그렇지 않으면 true를 return 하도록 solution 함수를 작성해주세요 (참고)
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;

bool solution(vector<string> phone_book)
{
	bool answer = true;
	// 전화번호별 출현 횟수를 저장하는 해시맵
	unordered_map<string, int> num_count;
	// 각 전화번호가 출현한 횟수를 계산
	for (auto num : phone_book)
		num_count[num]++;

	// 각 전화번호의 접두사가 다른 전화번호에 포함되는지 검사
	for (int i = 0; i < phone_book.size(); i++)
	{
		// 현재 전화번호의 접두사
		string prefix = "";
		for (int j = 0; j < phone_book[i].size(); j++)
		{
			prefix += phone_book[i][j];
			// 접두사가 다른 전화번호의 전체번호라면 false 반환
			if (num_count[prefix] && prefix != phone_book[i])
				return false;
		}
	}

	return answer;
}

01-2 의상 ( 프로그래머스 고득점 Kit, 해시 )

  • 코니가 가진 의상들이 담긴 2차원 배열 clothes가 주어질 때 서로 다른 옷의 조합의 수를 return 하도록 solution 함수를 작성해주세요 (참고)
#include <string>
#include <vector>
#include <unordered_map>

using namespace std;

int solution(vector<vector<string>> clothes)
{
    int answer = 1;
    unordered_map<string, int> mClothes;
    
    // 모든 종류를 체크한다
    for(auto cloth : clothes)
    {
        mClothes[cloth[1]] = mClothes[cloth[1]] + 1;
    }
    
    // 만약, 상의 3개, 하의 2개, 장신구 2개가 있었다고 가정하면, 총 가능한 경우의 수는 (3+1) * (2+1) * (2+1) = 36가지 이다
    // 참고로 (3+1) * (2+1) * (2+1) = 36가지 에서 +1은 선택 안함에 해당하는 경우의 수이다 ( 트리 구조로 그림을 그려보자 )
    for(auto it = mClothes.begin(); it != mClothes.end(); it++)
    {
        answer = answer * (it->second + 1);
    }
    
    // 그런데, 이 경우에는 (상의 선택 안 함, 하의 선택 안 함, 장신구 선택 안 함)이 포함되어 있으므로 그 경우를 빼주는 것이다
    // 덧 붙이자면, 문제의 제한사항에 스파이는 하루에 최소 한 개의 의상은 입습니다 라고 했다
    return answer - 1;
}

01-3 올바른 괄호 ( 프로그래머스 고득점 Kit, 스택/큐 )

  • 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요 (참고)
#include <string>
#include <iostream>
#include <stack>

using namespace std;

bool solution(string s)
{
    stack<char> stk;
    
    for(int i = 0; i < s.size(); ++i)
    {
        // 첫번째 문자가 '(' 이면 그냥 넣는다
        if(s[i] == '(')
            stk.push('(');
        // 첫번째 문자가 '(' 가 아닌데 스택이 비어 있다면 false를 리턴한다
        else if(stk.empty())
            return false;
        // 첫번째 문자가 '(' 가 아닌데 스택이 비어 있지 않다면 pop 한다
        else
            stk.pop();
    }
    
    return stk.size() == 0;
}

01-4 기능개발 ( 프로그래머스 고득점 Kit, 스택/큐 )

  • 먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요 (참고)
#include <string>
#include <vector>
#include <queue>

using namespace std;

vector<int> solution(vector<int> progresses, vector<int> speeds)
{
    // 정답을 담을 곳
    vector<int> answer;
    // 배포될 작업을 임시로 담을 곳
    queue<int> q;
    // 작업 개수라고 보면 되는 사이즈
    int size = progresses.size();
    
    // 작업에 해당하는 인덱스를 담는다
    for (int i = 0; i < size; ++i)
    {
        q.push(i);
    }
    
    // 큐에 인덱스 값이 남아 있으면 없을때 까지 돈다
    while(!q.empty())
    {
        int cnt = 0;
        
        // 작업 진척도 퍼센테이지를 담아놓은 벡터의 값을 증가시킨다
        for(int j = 0; j < size; ++j)
        {
            progresses[j] += speeds[j];
        }
        
        // 여기서 각 배포마다 몇 개의 기능이 배포되는지를 카운트 한다
        while(!q.empty() && 100 <= progresses[q.front()])
        { 
            ++cnt;
            q.pop();
        }
        
        // 각 배포마다 몇 개의 기능이 배포되는지 정답지에 담는다
        if(cnt != 0)
            answer.push_back(cnt);  
    }
    
    return answer;
}

01-5 프로세스 ( 프로그래머스 고득점 Kit, 스택/큐 )

  • 해당 프로세스가 몇 번째로 실행되는지 return 하도록 solution 함수를 작성해주세요 (참고)
#include <string>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

int solution(vector<int> priorities, int location)
{
    int answer = 0, count = 0;
    //일반큐
    queue<pair<int, int>> que;
    //우선순위 큐
    priority_queue<int> prio_que;
    //우선순위 큐 정렬 반대로
    //priority_queue<int, vector<int>, greater<int>> prio_que;
 
    for (int i = 0; i < priorities.size(); ++i)
    {
        //큐에 들어온 순서와 중요도를 넣음
        que.push(make_pair(i, priorities[i]));
        //우선순위 큐에 중요도를 넣음
        prio_que.push(priorities[i]);
    }

    //큐가 빌때까지 반복
    while (!que.empty())
    {
        int index = que.front().first;
        int value = que.front().second;
        que.pop();
        //우선순위 1순위와 현재값이 같다면
        if (prio_que.top() == value)
        {
            //우선순위큐 pop
            prio_que.pop();
            //하나가 나갔으므로 count
            count++;
            //현재 나간것이 원하는것과 같다면
            if (index == location)
            {
                //현재 카운터를 리턴
                answer = count;
                break;
            }
        }
        //우선순위 1순위와 현재값이 같지않다면 큐 뒤에 넣기
        else
            que.push(make_pair(index, value));
    }

    return answer;
}

01-6 다리를 지나는 트럭 ( 프로그래머스 고득점 Kit, 스택/큐 )

  • 이때 모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 return 하도록 solution 함수를 완성하세요 (참고)
#include <string>
#include <vector>
#include <queue>

using namespace std;

int solution(int bridge_length, int weight, vector<int> truck_weights)
{
    queue<int> que;
    int time = 0;
    int sum = 0;
    int truckIdx = 0;
    
    while(true)
    {
        // 1초인 시점부터 트럭이 진입하기 시작하자나 그러니까 시작하자 마자 ++ 시켜준거다
         time++;
        // 트럭의 무게가 다리의 무게보다 작으면, 트럭을 삽입
        if(sum + truck_weights[truckIdx] <= weight)
        {
            // 마지막 트럭이 삽입되면 종료
            if(truckIdx == truck_weights.size()-1)
            {
                // 마지막 트럭 도착시간 추가
                time += bridge_length;
                break;
            }
            
            que.push(truck_weights[truckIdx]);
            sum += truck_weights[truckIdx];
            truckIdx++;
        }
        // 트럭의 무게가 다리의 무게보다 크면, 0을 삽입해서 트럭을 도착점으로 민다
        else
        {
            que.push(0);
        }

        // 큐 사이즈 = 다리길이 -> 트럭 도착
        // ( 이 상황에서 트럭이 도착했다는 처리를 해줘야 다음 time++ 에서 새로운 트럭이 들어올 수 있다 )
        if(que.size() == bridge_length)
        {
            sum -= que.front();
            que.pop();
        }
    }
    return time;
}

01-7 주식가격 ( 프로그래머스 고득점 Kit, 스택/큐 )

  • 초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 return 하도록 solution 함수를 완성하세요 (참고)
#include <string>
#include <vector>
#include <stack>
 
using namespace std;
 
vector<int> solution(vector<int> prices)
{
    // 7 8 9 7 8 9 7 8 9 이 숫자 세트가 예시로서 적당하다
    vector<int> answer(prices.size());
    stack<int> st;

    int size = prices.size();
    for (int i = 0; i < size; i++)
    {
        // 스택이 비어있지않고
        // 스택의 마지막 인덱스가 가리키는 가격이 현재 가격보다 크다면 ( 가격이 떨어 졌다면 )
        while (!st.empty() && prices[i] < prices[st.top()])
        {
            // 지금까지 쌓인 모든 스택 인덱스를 검사하여 얼마만에 가격이 떨어졌는지 죄다 기록한다
            answer[st.top()] = i - st.top();
            // 가격이 떨어진 인덱스는 제거하자
            st.pop();
        }
        // 현재 인덱스를 스택에 넣기
        st.push(i);
    }

    // 스택이 빌때까지 반복
    while (!st.empty())
    {
        // 가격을 버텨낸 인덱스들이 얼마나 버텼는지 기록해준다
        // 유의사항1. 위에서 특정위치에 이미값을 넣었으므로 pushback이나 insert로하면 안된다.
        // 유의사항2. 뒤에서부터 넣어야하므로 size-1 에서 버텨낸 인덱스값을 빼준다.
        answer[st.top()] = (size - 1) - st.top();
        st.pop();
    }

    return answer;
}

01-8 더 맵게 ( 프로그래머스 고득점 Kit, 힙 )

  • Leo가 가진 음식의 스코빌 지수를 담은 배열 scoville과 원하는 스코빌 지수 K가 주어질 때, 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 섞어야 하는 최소 횟수를 return 하도록 solution 함수를 작성해주세요 (참고)
#include <string>
#include <vector>
#include<queue>

using namespace std;

int solution(vector<int> scoville, int K)
{
	int answer = 0;
	priority_queue<int, vector<int>, greater<int>> pq(scoville.begin(), scoville.end());
	while (pq.size() > 1 && pq.top() < K)
	{
		// 스코빌 지수가 가장 낮은 음식과 두번째로 낮은 음식의 스코빌 지수를 섞는다
		int first = pq.top();
		pq.pop();
		int second = pq.top();
		pq.pop();
		pq.push(first + (second * 2));

		// 섞은 횟수를 ++ 한다
		answer++;
	}

	// 스코빌 지수를 K 이상으로 만들 수 없을 경우 -1로 answer를 갱신한다
	if (pq.top() < K) answer = -1;

	return answer;
}

01-9 입국심사 ( 프로그래머스 고득점 Kit, 이분탐색 )

  • 모든 사람이 심사를 받는데 걸리는 시간의 최솟값을 return 하도록 solution 함수를 작성해주세요 (참고)
#include<bits/stdc++.h> // 필요한 헤더 파일들을 한번에 include하기 위한 코드

using namespace std;

long long solution(int n, vector<int> times)
{
	long long answer = 0; // 최종적으로 반환될 정답

	// times 배열을 오름차순으로 정렬하여 가장 큰 값이 times.back()에 위치하도록 함
	sort(times.begin(), times.end());

	long long left = 1; // 이진 탐색을 위한 왼쪽 끝 값
	long long right = (long long)times.back() * n; // 이진 탐색을 위한 오른쪽 끝 값

	while (left <= right) // 이진 탐색
	{
		long long mid = (left + right) / 2; // 중앙 값

		long long cnt = 0; // mid 시간 동안 심사 가능한 인원 수
		for (long long i = 0; i < times.size(); i++) // 모든 심사관이 mid 시간 동안 처리할 수 있는 인원 수를 합산
		{
			cnt = cnt + (mid / times[i]);
		}

		if (cnt < n) // 처리 가능한 인원 수가 n보다 작은 경우
		{
			left = mid + 1; // 이진 탐색의 왼쪽 끝 값을 mid+1로 조정하여 다시 탐색
		}
		else // 처리 가능한 인원 수가 n 이상인 경우
		{
			answer = mid; // 현재 시간을 정답에 저장
			right = mid - 1; // 이진 탐색의 오른쪽 끝 값을 mid-1로 조정하여 다시 탐색
		}
	}

	return answer; // 정답 반환
}


Chapter 02 시뮬레이션 ( 중요도 상 )

02-1 뱀 ( 3190 )

  • 첫째 줄에 게임이 몇 초에 끝나는지 출력한다 (참고)
#include <iostream>
#include <algorithm>
#include <deque>
#include <vector>
using namespace std;

int board_size; // 게임판의 크기
int num_of_apple; // 먹이의 개수
int num_of_order; // 뱀의 이동 방향 변경 횟수

deque<pair<int, int>> snake_body; // 뱀의 몸통 좌표
vector<int> order_time; // 뱀의 이동 방향 변경 시간
vector<char> order_direction; // 뱀의 이동 방향 변경 방향

int game_board[100][100]; // 게임판 상태
int dx[] = { 1, 0, -1, 0 }; // x좌표 이동 방향
int dy[] = { 0, 1, 0, -1 }; // y좌표 이동 방향

// 뱀 게임 실행 함수
int run_game(int start_x, int start_y)
{
	int direction = 0; // 초기 방향은 오른쪽
	int cnt = 0; // 게임 진행 시간
	int x, y; // 뱀 머리 좌표
	int order_idx = 0; // 뱀의 이동 방향 변경 명령 인덱스

	snake_body.push_front(make_pair(start_x, start_y)); // 뱀 머리 좌표 큐에 추가
	game_board[start_y][start_x] = 2; // 뱀이 있는 위치 표시

	while (1)
	{
		x = snake_body.front().first;
		y = snake_body.front().second;

		cnt++;

		int next_x = x + dx[direction];
		int next_y = y + dy[direction];

		// 게임 종료 조건
		if (next_x < 0 || next_y < 0 || board_size <= next_x || board_size <= next_y || game_board[next_y][next_x] == 2)
			return cnt;

		snake_body.push_front(make_pair(next_x, next_y)); // 뱀 머리를 다음 위치로 이동

		// 먹이를 먹지 않은 경우
		if (game_board[next_y][next_x] != 1)
		{
			int tail_x = snake_body.back().first;
			int tail_y = snake_body.back().second;
			game_board[tail_y][tail_x] = 0; // 꼬리 제거
			snake_body.pop_back(); // 뱀 꼬리 제거
		}

		// 먹이를 먹지 않는 경우를 먼저 처리 해주지 않으면 아래의 코드에서 표시해준게 없어질 수 있다
		game_board[next_y][next_x] = 2; // 뱀이 있는 위치 표시

		// 뱀의 이동 방향 변경
		if (cnt == order_time[order_idx])
		{
			if (order_direction[order_idx] == 'L')
			{ // 왼쪽으로 회전
				direction += 3;
				direction %= 4;
			}
			else
			{ // 오른쪽으로 회전
				direction += 1;
				direction %= 4;
			}

			if (order_idx < order_time.size() - 1) order_idx++;
		}
	}
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);

	cin >> board_size;

	// 사과
	cin >> num_of_apple;
	for (int i = 0; i < num_of_apple; i++)
	{
		int applex, appley;
		cin >> applex >> appley;
		game_board[applex - 1][appley - 1] = 1;
	}

	// 명령
	cin >> num_of_order;
	for (int i = 0; i < num_of_order; i++)
	{
		int t;
		char d;
		cin >> t >> d;
		order_time.push_back(t);
		order_direction.push_back(d);
	}

	cout << run_game(0, 0);
	return 0;
}

02-2 주사위 굴리기 ( 14499 )

  • 이동할 때마다 주사위의 윗 면에 쓰여 있는 수를 출력한다 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다 (참고)
// https://excited-hyun.tistory.com/215 참고

#include<bits/stdc++.h>
using namespace std;

int mymap[20][20];
int X[5] = { 0, 1, -1, 0, 0 };
int Y[5] = { 0, 0, 0, -1, 1 };
vector<int> dice(6, 0);

int main()
{
	int n, m, x, y, k;

	scanf("%d %d %d %d %d", &n, &m, &y, &x, &k);
	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < m; ++j)
			scanf("%d", &mymap[i][j]);
	}

	int move;
	for (int i = 0; i < k; ++i)
	{
		scanf("%d", &move);
		int newX = x + X[move];
		int newY = y + Y[move];

		if (newX < 0 || newY < 0 || m <= newX || n <= newY)
			continue;

		if (move == 1)
			dice = { dice[3], dice[1], dice[0], dice[5], dice[4], dice[2] };
		else if (move == 2)
			dice = { dice[2], dice[1], dice[5], dice[0], dice[4], dice[3] };
		else if (move == 3)
			dice = { dice[4], dice[0], dice[2], dice[3], dice[5], dice[1] };
		else if (move == 4)
			dice = { dice[1], dice[5], dice[2], dice[3], dice[0], dice[4] };

		if (mymap[newY][newX] == 0)
			mymap[newY][newX] = dice[5];
		else
		{
			dice[5] = mymap[newY][newX];
			mymap[newY][newX] = 0;
		}

		cout << dice[0] << endl;

		x = newX;
		y = newY;
	}

	return 0;
}

02-3 로봇 청소기 ( 14503 )

  • 로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오 (참고)
// https://blog.naver.com/PostView.naver?blogId=fbfbf1&logNo=222472445249&categoryNo=34&parentCategoryNo=37&viewDate=&currentPage=1&postListTopCurrentPage=1&from=postView 참고

#include<bits/stdc++.h>
using namespace std;

int board[52][52];
bool check[52][52];

int dy[4] = { -1, 0, 1, 0 };
int dx[4] = { 0, 1, 0, -1 };
int sum = 0;

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

	int N, M, r, c, d;
	cin >> N >> M >> r >> c >> d;
	for (int i = 0; i < N; ++i)
	{
		for (int j = 0; j < M; ++j)
		{
			cin >> board[i][j];
		}
	}

	while (true)
	{
		if (check[r][c] == false)
		{
			check[r][c] = true;
			sum++;
		}

		bool move = false;
		for (int i = 0; i < 4; ++i)
		{
			d = (d + 3) % 4;
			int nx = c + dx[d];
			int ny = r + dy[d];

			if (nx < 0 || ny < 0 || M <= nx || N <= ny) continue;

			if (board[ny][nx] == 0 && check[ny][nx] == false)
			{
				c = nx;
				r = ny;
				move = true;
				break;
			}
		}

		if (!move)
		{
			int back = (d + 2) % 4;
			int nx = c + dx[back];
			int ny = r + dy[back];

			if (nx < 0 || ny < 0 || M <= nx || N <= ny) break;
			if (board[ny][nx] == 1) break;

			c = nx;
			r = ny;
		}
	}

	cout << sum << "\n";
}

02-4 톱니바퀴 (2) ( 15662 )

  • 톱니바퀴 T개의 초기 상태와 톱니바퀴를 회전시킨 방법이 주어졌을 때, 최종 톱니바퀴의 상태를 구하는 프로그램을 작성하시오 (참고)
// https://intaehwang.tistory.com/108 참고

#include<bits/stdc++.h>
using namespace std;

int gear[1001][8];
int check[1001];
 
int main()
{
    int t;
    cin >> t;
 
    for (int i = 1; i <= t; i++)
    {
        for (int j = 0; j < 8; j++)
            scanf("%1d", &gear[i][j]);
    }
 
    int k;
    cin >> k;
    for (int i = 0; i < k; i++)
    {
        memset(check, 0, sizeof(check));
        int x, y;
        cin >> x >> y;
        check[x] = y;
        
        for (int j = x; j < t; j++)
        {
            if (gear[j][2] != gear[j+1][6]) check[j+1] = -check[j];
            else break;
        }
        for (int j = x; 1 < j; j--)
        {
            if (gear[j-1][2] != gear[j][6]) check[j-1] = -check[j];
            else break;
        }
        for (int j = 1; j <= t; j++)
        {
            if (check[j] == 0) continue;
            else if (check[j] == 1)
            {
                int tmp = gear[j][7];
                for(int cur = 7; 0 < cur; --cur) gear[j][cur] = gear[j][cur-1];
                gear[j][0] = tmp;
            }
            else if (check[j] == -1)
            {
                int tmp = gear[j][0];
                for(int cur = 0; cur < 7; ++cur) gear[j][cur] = gear[j][cur+1];
                gear[j][7] = tmp;
            }
        }
    }
    
    int ans = 0;
    for (int i = 1; i <= t; i++)
        if (gear[i][0] == 1) ans += 1;
    
    cout << ans << endl;
}

02-5 드래곤 커브 ( 15685 )

  • 크기가 100×100인 격자 위에 드래곤 커브가 N개 있다. 이때, 크기가 1×1인 정사각형의 네 꼭짓점이 모두 드래곤 커브의 일부인 정사각형의 개수를 구하는 프로그램을 작성하시오 (참고)
// https://velog.io/@sj-lee33/%EB%B0%B1%EC%A4%80-15685-%EB%93%9C%EB%9E%98%EA%B3%A4-%EC%BB%A4%EB%B8%8C-c-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%92%80%EC%9D%B4 참고

#include<bits/stdc++.h>
using namespace std;
#define MAX 101

int N;
int x, y, d, g, cnt;
int mymap[MAX][MAX];
int dx[4] = { 1, 0, -1, 0 };
int dy[4] = { 0, -1, 0, 1 };
vector<int> direction;

void Make_Dragon_Curve()
{
	int size = direction.size();
	for (int i = size - 1; 0 <= i; i--)
	{
		int temp = (direction[i] + 1) % 4;
		x += dx[temp];
		y += dy[temp];
		mymap[y][x] = 1;
		direction.push_back(temp);
	}
}

void Count_Square()
{
	for (int i = 0; i < MAX; i++)
	{
		for (int j = 0; j < MAX; j++)
		{
			if (mymap[i][j] == 1 && mymap[i][j + 1] == 1 && mymap[i + 1][j + 1] == 1 && mymap[i + 1][j] == 1)
				cnt++;
		}
	}
}

void Solution()
{
	cin >> N;

	while (N--)
	{
		direction.clear();
		cin >> x >> y >> d >> g;

		// initialize
		mymap[y][x] = 1;

		// generation 0
		x += dx[d];
		y += dy[d];
		mymap[y][x] = 1;
		direction.push_back(d);

		// next 'g' generations
		while (g--)
		{
			Make_Dragon_Curve();
		}
	}

	Count_Square();
	cout << cnt << endl;
}

int main()
{
	Solution();
}

02-6 인구 이동 ( 16234 )

  • 각 나라의 인구수가 주어졌을 때, 인구 이동이 며칠 동안 발생하는지 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

int arr[55][55];
bool visited[55][55];
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
int n, l, r, uni, sum;
queue<pair<int, int>> que;

// y, x 좌표를 시작으로 DFS 탐색을 하면서 인구 이동이 가능한 국가들을 모두 찾아줌
void dfs(int y, int x)
{
	// 시작 좌표를 큐에 추가하고 방문 체크를 함
	que.push({ y, x });
	visited[y][x] = true;
	sum = sum + arr[y][x];
	uni++;

	// 현재 위치에서 4방향으로 이동하면서 인구 이동이 가능한 국가들을 찾음
	for (int i = 0; i < 4; i++)
	{
		int ny = y + dy[i], nx = x + dx[i];
		if (nx < 0 || ny < 0 || n <= nx || n <= ny || visited[ny][nx]) continue;

		if (l <= abs(arr[y][x] - arr[ny][nx]) && abs(arr[y][x] - arr[ny][nx]) <= r)
			dfs(ny, nx);
	}
}

int main()
{
	scanf("%d %d %d", &n, &l, &r);
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++) scanf("%d", &arr[i][j]);

	int ret = 0; bool move = false;
	do
	{
		move = false;
		memset(visited, 0, sizeof(visited));

		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				// 아직 방문하지 않은 국가를 시작으로 DFS 탐색을 시작함
				uni = 0;
				if (!visited[i][j])
				{
					sum = 0;
					dfs(i, j);

					// 큐에 저장된 모든 국가들의 인구수를 현재 연합의 인구수 평균값으로 업데이트함
					while (!que.empty())
					{
						auto pos = que.front();
						que.pop();
						arr[pos.first][pos.second] = sum / uni;
					}

					// 인구 이동이 발생한 경우 move를 true로 세팅
					if (sum != arr[i][j]) move = true;
				}
			}
		}
		// 인구 이동이 발생한 경우 날짜를 증가시킨다
		if (move) ret++;

	} while (move);

	printf("%d", ret);
}
// 이 코드는 도대체 어떻게 돌아가는지 연구가 필요하다
// for (int i = 0; i < 4; i++) 여기 부분에서
// sum = arr[ny][nx] + dfs(ny, nx); 이 코드가 말이 안된다
#include<bits/stdc++.h>
using namespace std;

int arr[55][55];
bool visited[55][55];
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
int n, l, r, uni, sum;
queue<pair<int, int>> que;

// y, x 좌표를 시작으로 DFS 탐색을 하면서 인구 이동이 가능한 국가들을 모두 찾아줌
int dfs(int y, int x)
{
	// 시작 좌표를 스택에 추가하고 방문 체크를 함
	que.push({ y, x });
	visited[y][x] = true;
	uni++;

	// 현재 위치에서 4방향으로 이동하면서 인구 이동이 가능한 국가들을 찾음
	for (int i = 0; i < 4; i++)
	{
		int ny = y + dy[i], nx = x + dx[i];
		if (nx < 0 || ny < 0 || n <= nx || n <= ny || visited[ny][nx]) continue;
		if (abs(arr[y][x] - arr[ny][nx]) <= r && l <= abs(arr[y][x] - arr[ny][nx])) sum = arr[ny][nx] + dfs(ny, nx);
	}

	return sum;
}

int main()
{
	scanf("%d %d %d", &n, &l, &r);
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++) scanf("%d", &arr[i][j]);

	int ret = 0; bool move = false;
	do
	{
		move = false;
		memset(visited, 0, sizeof(visited));

		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				// 아직 방문하지 않은 국가를 시작으로 DFS 탐색을 시작함
				uni = 0;
				if (!visited[i][j])
				{
					sum = 0;
					sum = arr[i][j] + dfs(i, j);

					// 큐에 저장된 모든 국가들의 인구수를 현재 연합의 인구수 평균값으로 업데이트함
					while (!que.empty())
					{
						auto pos = que.front();
						que.pop();
						arr[pos.first][pos.second] = sum / uni;
					}

					// 인구 이동이 발생한 경우 카운트를 증가시킴
					if (sum != arr[i][j]) move = true;
				}
			}
		}
		// 인구 이동이 발생한 경우 날짜를 증가시킨다
		if (move) ret++;

	} while (move);

	printf("%d", ret);
}

02-7 미세먼지 안녕! ( 17144 )

  • 첫째 줄에 T초가 지난 후 구사과 방에 남아있는 미세먼지의 양을 출력한다 (참고)
#include <iostream>
#include <vector>
using namespace std;

int R, C, T;
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
int board[51][51];
vector<int> cleaner; // 공기청정기 위치

// 미세먼지 확산 함수
void CalDiffusion()
{
	vector<vector<int>> temp(51, vector<int>(51, 0)); // 확산될 미세먼지 임시 보관

	// 미세먼지 있는 칸 별로 확산되는 미세먼지 양 계산
	for (int i = 0; i < R; i++)
	{
		for (int j = 0; j < C; j++)
		{
			if (0 < board[i][j]) // 미세먼지 있는 칸
			{
				int dust = board[i][j] / 5; // 확산되는 미세먼지 양 계산
				if (dust == 0) continue; // 예시 그림을 보면 나누기 5를 했을때 1 이상 나오지 않으면 확산되지 않음

				int cnt = 0; // 확산된 칸 갯수
				for (int dir = 0; dir < 4; dir++) // 사방 탐색
				{
					int ny = i + dy[dir];
					int nx = j + dx[dir];

					if (nx < 0 || ny < 0 || C <= nx || R <= ny) continue; // 범위 체크
					if (board[ny][nx] == -1) continue; // 공기청정기 체크

					temp[ny][nx] = temp[ny][nx] + dust; // 미세먼지 확산
					cnt++;
				}

				// 확산 이후 미세먼지 감소
				board[i][j] = board[i][j] - (dust * cnt);
			}
		}
	}

	// 확산된 미세먼지와 칸 별로 줄어든 미세먼지 합산
	// 확산된 먼지가 다른 칸의 먼지양을 나누는 계산을
	// 할때 영향을 끼치면 안되서 이렇게 하는것으로 추정
	for (int i = 0; i < R; i++)
	{
		for (int j = 0; j < C; j++)
		{
			board[i][j] = board[i][j] + temp[i][j];
		}
	}
}

void CleanDust()
{
	// upward
	int cr = cleaner[0];
	for (int i = cr - 1; 0 < i; i--) board[i][0] = board[i - 1][0];
	for (int j = 0; j < C - 1; j++) board[0][j] = board[0][j + 1];
	for (int i = 0; i < cr; i++) board[i][C - 1] = board[i + 1][C - 1];
	for (int j = C - 1; 1 < j; j--) board[cr][j] = board[cr][j - 1];
	board[cr][1] = 0;

	// downward
	cr = cleaner[1];
	for (int i = cr + 1; i < R - 1; i++) board[i][0] = board[i + 1][0];
	for (int j = 0; j < C - 1; j++) board[R - 1][j] = board[R - 1][j + 1];
	for (int i = R - 1; cr < i; i--) board[i][C - 1] = board[i - 1][C - 1];
	for (int j = C - 1; 1 < j; j--) board[cr][j] = board[cr][j - 1];
	board[cr][1] = 0;
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(NULL);
	cin >> R >> C >> T;
	for (int i = 0; i < R; i++)
	{
		for (int j = 0; j < C; j++)
		{
			cin >> board[i][j];
			if (board[i][j] == -1) cleaner.push_back(i);
		}
	}

	while (T--)
	{
		CalDiffusion();
		CleanDust();
	}

	int answer = 0;
	for (int i = 0; i < R; i++)
	{
		for (int j = 0; j < C; j++)
		{
			if (board[i][j] > 0) answer += board[i][j];
		}
	}
	cout << answer;
}

02-8 이차원 배열과 연산 ( 17140 )

  • A[r][c]에 들어있는 값이 k가 되기 위한 연산의 최소 시간을 출력한다 100초가 지나도 A[r][c] = k가 되지 않으면 -1을 출력한다 (참고)
#include<bits/stdc++.h>
using namespace std;

int grid[101][101]; // 101x101 크기의 2차원 배열 선언
int rows, cols, target; // 행, 열, 타겟값
int rowCount = 3; // 현재 행의 개수
int colCount = 3; // 현재 열의 개수

void sortRows() // 각 행에 대해 정렬하는 함수
{
	int cnt = 0;
	for (int i = 0; i < rowCount; i++) // 모든 행에 대해 반복
	{
		int freq[101] = { 0 }; // 각 숫자의 등장 횟수를 저장하는 배열
		for (int j = 0; j < colCount; j++) // 현재 행의 모든 열에 대해 반복
		{
			freq[grid[i][j]]++; // 해당 숫자의 등장 횟수 증가
		}

		vector<pair<int, int>> freqPairs; // 등장 횟수와 해당 숫자를 저장하는 벡터
		for (int j = 1; j < 101; j++) // 1부터 100까지 모든 숫자에 대해 반복
		{
			if (0 < freq[j]) // 해당 숫자가 등장한 경우
			{
				freqPairs.push_back({ freq[j], j }); // 등장 횟수와 해당 숫자를 벡터에 추가
			}
		}

		sort(freqPairs.begin(), freqPairs.end()); // 등장 횟수를 기준으로 오름차순 정렬

		int idx = 0; // 현재 열 인덱스를 저장하는 변수
		int size = freqPairs.size(); // 등장한 숫자의 개수
		for (int j = 0; j < size; j++) // 등장한 숫자에 대해 반복
		{
			grid[i][idx] = freqPairs[j].second; // 해당 숫자를 현재 열에 저장
			grid[i][idx + 1] = freqPairs[j].first; // 해당 숫자의 등장 횟수를 다음 열에 저장
			idx += 2; // 열 인덱스를 2 증가
		}
		cnt = max(cnt, size * 2); // 열 개수를 갱신

		for (int j = idx; j < 101; j++) grid[i][j] = 0; // 남은 열을 0으로 초기화
	}

	colCount = cnt; // 열 개수를 업데이트
}

void sortCols()
{
	int cnt = 0;
	for (int i = 0; i < colCount; i++)
	{
		// freq: 열에서 각 숫자의 등장 횟수를 저장하는 배열
		int freq[101] = { 0 };
		for (int j = 0; j < rowCount; j++)
		{
			freq[grid[j][i]]++;
		}

		// freqPairs: 등장 횟수가 1 이상인 숫자와 그 횟수를 저장하는 벡터
		vector<pair<int, int>> freqPairs;
		for (int j = 1; j < 101; j++)
		{
			if (0 < freq[j])
			{
				freqPairs.push_back({ freq[j], j });
			}
		}

		// 등장 횟수가 적은 숫자부터 정렬
		sort(freqPairs.begin(), freqPairs.end());

		// idx: 현재 열의 어디까지 값을 넣었는지를 저장하는 인덱스
		int idx = 0;
		int size = freqPairs.size();
		for (int j = 0; j < size; j++)
		{
			grid[idx][i] = freqPairs[j].second;  // 숫자를 넣음
			grid[idx + 1][i] = freqPairs[j].first;  // 등장 횟수를 넣음
			idx += 2;
		}
		cnt = max(cnt, size * 2);  // 현재 가장 긴 열의 길이 업데이트

		// 나머지 빈 공간에는 0을 채움
		for (int j = idx; j < 101; j++) grid[j][i] = 0;
	}

	rowCount = cnt;  // 새로운 행의 개수를 업데이트
}

int main()
{
	int time = 0;

	// 입력 받기
	cin >> rows >> cols >> target;
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			cin >> grid[i][j];
		}
	}

	// 목표값이 나올 때까지 정렬을 반복
	while (time <= 100)
	{
		if (grid[rows - 1][cols - 1] == target) break; // 목표값이 나오면 종료
		if (colCount <= rowCount) sortRows(); // 행 기준으로 정렬
		else sortCols(); // 열 기준으로 정렬
		time++;
	}

	// 목표값을 찾지 못한 경우 -1 출력, 찾은 경우에는 시간 출력
	if (100 < time) cout << "-1";
	else cout << time;
}

02-9 컨베이어 벨트 위의 로봇 ( 20055 )

  • 몇 번째 단계가 진행 중일때 종료되었는지 출력한다 (참고)
// 이 문제에서 사용된 로직은 DP스러운 문제에서 필요한 경우가 있다

#include<bits/stdc++.h>
using namespace std;

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

	int N, K;
	cin >> N >> K;

	vector<bool> occupied(2 * N, false);
	vector<int> A(2 * N);
	for (auto& item : A)
		cin >> item;

	int counter = 0;
	while (true)
	{
		++counter;

		// 1.
		rotate(occupied.rbegin(), occupied.rbegin() + 1, occupied.rend());
		rotate(A.rbegin(), A.rbegin() + 1, A.rend());
		occupied[N - 1] = false;

		// 2.
		for (int i = N - 1; 0 < i; --i)
		{
			if (occupied[i - 1] && !occupied[i] && A[i])
			{
				swap(occupied[i - 1], occupied[i]);
				--A[i];
			}
		}
		occupied[N - 1] = false;

		// 3.
		if (A[0])
		{
			occupied[0] = true;
			--A[0];
		}

		// 4.
		if (K <= count_if(A.begin(), A.end(), [](int item) { return item == 0; }))
		{
			break;
		}
	}

	cout << counter;

	return 0;
}

02-10 마법사 상어와 파이어볼 ( 20056 )

  • 마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다 (참고)
// https://www.youtube.com/watch?v=g2B-rKwaVUQ 14 : 57 참고

#include<bits/stdc++.h>
using namespace std;

int N, M, K;

// 볼을 나타내는 구조체 선언
struct Ball
{
	int y, x;   // 좌표
	int m, s, d; // 질량, 속도, 방향
};

// 각 좌표에 위치한 Ball을 저장할 벡터 배열 선언
vector<Ball> balls;
vector<Ball> mat[53][53];

// 각 방향을 나타내는 배열 선언 ( 12시 방향부터 시계 방향으로  )
int dy[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
int dx[] = { 0, 1, 1, 1, 0, -1, -1, -1 };

void solution()
{
	while (K--)
	{
		// Ball 움직이기  
		for (int i = 0; i < balls.size(); i++)
		{
			int y = balls[i].y;
			int x = balls[i].x;
			int m = balls[i].m;
			int s = balls[i].s;
			int d = balls[i].d;

			// 현재 속력으로 이동한 거리 계산
			int move = s % N;
			int ny = y + dy[d] * move; // 새로운 y 좌표
			int nx = x + dx[d] * move; // 새로운 x 좌표

			// 경계를 넘어갈 경우 위치 수정
			if (ny < 0) ny = ny + N;
			if (nx < 0) nx = nx + N;
			if (N <= ny) ny = ny - N;
			if (N <= nx) nx = nx - N;

			// 새로운 위치에 Ball 추가
			mat[ny][nx].push_back({ ny, nx, m, s, d });
		}


		// 움직임이 완료된 Ball들 합치기  
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < N; j++)
			{
				if (2 <= mat[i][j].size())
				{
					int sum_m = 0, sum_s = 0;
					int odd = 0, even = 0;
					int size = mat[i][j].size();

					// Ball의 속력, 방향, 질량의 합 구하기
					for (int t = 0; t < size; t++)
					{
						sum_m = sum_m + mat[i][j][t].m;
						sum_s = sum_s + mat[i][j][t].s;

						if (mat[i][j][t].d % 2 == 0) even++;
						else odd++;
					}

					mat[i][j].clear(); // 합쳐진 Ball 제거

					// 소멸될 예정인 파이어볼은 미리 거르자
					if (sum_m / 5 == 0) continue;

					// 방향 결정
					if (odd == 0 || even == 0)
					{
						for (int nd = 0; nd < 7; nd = nd + 2)
						{
							mat[i][j].push_back({ i, j, sum_m / 5, sum_s / size, nd });
						}
					}
					else
					{
						for (int nd = 1; nd < 8; nd = nd + 2)
						{
							mat[i][j].push_back({ i, j, sum_m / 5, sum_s / size, nd });
						}
					}
				}
			}
		}

		// 합치기가 완료된 Ball을 저장하기  
		balls.clear();
		for (int i = 0; i < N; i++)
		{
			for (int j = 0; j < N; j++)
			{
				for (int t = 0; t < mat[i][j].size(); t++)
				{
					balls.push_back(mat[i][j][t]);
				}
				mat[i][j].clear();
			}
		}
	}

	// 남은 질량의 합 구하기  
	int ans = 0;
	for (int i = 0; i < balls.size(); i++)
		ans = ans + balls[i].m;

	cout << ans << endl;
}

int main(void)
{
	cin >> N >> M >> K;
	for (int i = 0; i < M; i++)
	{
		int y, x, m, s, d;
		cin >> y >> x >> m >> s >> d;
		balls.push_back({ y, x, m, s, d });
	}

	solution();
}
// 킵 해두자
// https://kibbomi.tistory.com/193

02-11 상어 초등학교 ( 21608 )

  • 첫째 줄에 학생의 만족도의 총 합을 출력한다 (참고)
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;

int main()
{
	//위,아래,왼,우 방향 배열
	int x_direction[] = { 1, -1, 0, 0 };
	int y_direction[] = { 0, 0, 1, -1 };
	int answer = 0;
	int n;

	// n 입력 받기
	cin >> n;

	// n x n 크기의 2차원 벡터 seats를 만들고 0으로 초기화
	vector<vector<int>> seats(n, vector<int>(n, 0));

	// n x n + 1 크기의 2차원 벡터 like와 1차원 벡터 students를 만들기
	vector<vector<int>> like(n * n + 1, vector<int>(4, 0));
	vector<int> students(n * n + 1);

	// 학생들의 좋아하는 학생 번호 입력받기
	for (int i = 0; i < n * n; i++)
	{
		cin >> students[i];
		for (int j = 0; j < 4; j++)
			cin >> like[students[i]][j];
	}

	// 배치 시작
	for (int s = 0; s < n * n; s++)
	{
		int student = students[s];
		int most_like = -1, most_empty = -1, selected_y = -1, selected_x = -1;

		// 각 자리마다 좋아하는 학생과 인접한 빈 자리 수 계산하기
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
			{
				// 이미 학생이 있는 자리는 건너뛰기
				if (seats[i][j] != 0) continue;

				int cur_like = 0, cur_empty = 0;
				for (int dir = 0; dir < 4; dir++)
				{
					int ny = i + y_direction[dir], nx = j + x_direction[dir];
					// 범위를 벗어나는 경우 건너뛰기
					if (nx < 0 || ny < 0 || n <= nx || n <= ny) continue;

					if (seats[ny][nx] == 0)
					{
						cur_empty++;
						continue;
					}

					// 좋아하는 학생인 경우 인접한 학생 수 1 증가
					for (int k = 0; k < 4; k++)
					{
						if (seats[ny][nx] == like[student][k])
						{
							cur_like++;
							break;
						}
					}
				}

				// 조건에 맞는 자리 선택하기
				if (most_like < cur_like)
				{
					most_like = cur_like, most_empty = cur_empty;
					selected_y = i, selected_x = j;
				}
				else if (most_like == cur_like)
				{
					if (most_empty < cur_empty)
					{
						most_empty = cur_empty;
						selected_y = i, selected_x = j;
					}
				}
			}

		// 선택된 자리에 학생 배치하기
		seats[selected_y][selected_x] = student;
	}

	// 만족도 계산
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		{
			int student = seats[i][j];
			int satisfaction = 0;

			// 상하좌우 인접한 자리에 좋아하는 학생이 있으면 만족도 증가
			for (int dir = 0; dir < 4; dir++)
			{
				int ny = i + y_direction[dir], nx = j + x_direction[dir];
				if (nx < 0 || ny < 0 || n <= nx || n <= ny) continue;

				for (int k = 0; k < 4; k++)
				{
					if (seats[ny][nx] == like[student][k])
					{
						satisfaction++;
						break;
					}
				}
			}

			// 만족도가 1 이상이면 answer에 추가
			if (satisfaction)
			{
				int ans = 1;
				for (int score = 0; score < satisfaction - 1; score++) ans = ans * 10;
				answer = answer + ans;
			}
		}

	cout << answer << endl;
	return 0;
}
// https://gusdnr69.tistory.com/296 킵 해두자

02-12 마법사 상어와 비바라기 ( 21610 )

  • 첫째 줄에 M번의 이동이 모두 끝난 후 바구니에 들어있는 물의 양의 합을 출력한다 (참고)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

int main()
{
	int gridSize, numMoves;
	int answer = 0;
	queue<pair<int, int>> cloudQueue;
	vector<vector<int>> grid(51, vector<int>(51, 0));
	int directionY[9] = { 0, 0, -1, -1, -1, 0, 1, 1, 1 };
	int directionX[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };

	// 그리드 사이즈와 이동 횟수 입력받기
	cin >> gridSize >> numMoves;

	// 그리드 초기화
	for (int i = 1; i <= gridSize; i++)
		for (int j = 1; j <= gridSize; j++)
			cin >> grid[i][j];

	// 초기 구름 위치 설정
	cloudQueue.push(make_pair(gridSize, 1));
	cloudQueue.push(make_pair(gridSize, 2));
	cloudQueue.push(make_pair(gridSize - 1, 1));
	cloudQueue.push(make_pair(gridSize - 1, 2));

	// 이동 횟수만큼 반복
	while (numMoves--)
	{
		int moveDirection, moveDistance, queueSize;
		vector<vector<bool>> visited(51, vector<bool>(51, false));
		vector<pair<int, int>> cloudPositions;

		// 이동 방향과 거리 입력받기
		cin >> moveDirection >> moveDistance;

		// 현재 구름 위치에서 이동 방향과 거리에 따라 이동하기
		queueSize = cloudQueue.size();
		for (int i = 0; i < queueSize; i++)
		{
			int y = cloudQueue.front().first;
			int x = cloudQueue.front().second;
			cloudQueue.pop();

			for (int j = 0; j < moveDistance; j++)
			{
				x += directionX[moveDirection];
				y += directionY[moveDirection];

				// 그리드를 벗어나는 경우 위치 조정
				if (x == 0)
					x = gridSize;
				else if (gridSize < x)
					x = 1;

				if (y == 0)
					y = gridSize;
				else if (gridSize < y)
					y = 1;
			}

			grid[y][x]++;
			cloudPositions.push_back(make_pair(y, x));
			visited[y][x] = true;
		}

		// 현재 구름 위치에서 대각선 방향으로 인접한 칸의 물의 양 증가시키기
		for (int i = 0; i < cloudPositions.size(); i++)
		{
			int y = cloudPositions[i].first;
			int x = cloudPositions[i].second;

			for (int j = 2; j <= 8; j += 2)
			{
				int nx = x + directionX[j];
				int ny = y + directionY[j];

				if (1 <= nx && 1 <= ny && nx <= gridSize && ny <= gridSize && 1 <= grid[ny][nx])
					grid[y][x]++;
			}
		}

		// 구름을 이동시킨 후 물의 양이 2 이상이 되는 칸에 새로운 구름 생성
		for (int i = 1; i <= gridSize; i++)
			for (int j = 1; j <= gridSize; j++)
				if (2 <= grid[i][j] && !visited[i][j])
				{
					cloudQueue.push(make_pair(i, j));
					grid[i][j] -= 2;
				}
	}

	// 최종적으로 남아있는 물의 양 계산
	for (int i = 1; i <= gridSize; i++)
		for (int j = 1; j <= gridSize; j++)
			if (0 < grid[i][j])
				answer += grid[i][j];

	// 결과 출력
	cout << answer;

	return 0;
}
// https://eunchanee.tistory.com/398 킵 해두자
// https://imnotabear.tistory.com/562 // 킵 해두자


Chapter 03 그래프 ( 중요도 중 )

03-1 전력망을 둘로 나누기 ( 프로그래머스 고득점 Kit, 완전탐색 )

  • 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요 (참고)
// 기본 그래프 문제 5개

#include <bits/stdc++.h>
using namespace std;

const int INF = 1e9;
const int MAX = 101;

// 각 노드는 중복이 존재할 수 없다
set<int> graph[MAX];

int getLength(int nodecount, int node)
{
    int length = 0;
    queue<int> que;
    
    // 현재 노드 세팅
    que.push(node);
    
    // 메모이제이션 기법으로 중복 계산 방지
    vector<bool> visited(nodecount);
    visited[node] = true;
    
    while (!que.empty())
    {
        // 다음 노드 세팅
        int cur = que.front();
        que.pop();
        length++;
        
        // 현재 노드와 연결된 노드를 모두 큐에 등록한다
        for (auto next : graph[cur])
        {
            if (visited[next])
                continue;
            
            que.push(next);
            visited[next] = true;
        }
    }

    return length;
}

int solution(int n, vector<vector<int>> wires)
{
    // 엣지들을 서로 연결
    for (auto wire : wires)
    {
        graph[wire[0]].insert(wire[1]);
        graph[wire[1]].insert(wire[0]);
    }
    
    int answer = INF;
    
    // 모든 간선을 한번씩 끊어가면서, 노드 개수의 차이가 최소가 되는 값을 구하자
    for (auto wire : wires)
    {
        graph[wire[0]].erase(wire[1]);
        graph[wire[1]].erase(wire[0]);
        
        answer = min(answer, abs(getLength(n, wire[0]) - getLength(n, wire[1])));
        
        graph[wire[0]].insert(wire[1]);
        graph[wire[1]].insert(wire[0]);
    }
    
    return answer;
}

03-2 가장 먼 노드 ( 프로그래머스 고득점 Kit, 그래프 )

  • 노드의 개수 n, 간선에 대한 정보가 담긴 2차원 배열 vertex가 매개변수로 주어질 때, 1번 노드로부터 가장 멀리 떨어진 노드가 몇 개인지를 return 하도록 solution 함수를 작성해주세요 (참고)
#include <bits/stdc++.h>
using namespace std;

int maxLevel = 1;

int solution(int n, vector<vector<int>> edges)
{
	int answer = 0;

	vector<vector<int>> graph(n + 1);
	vector<bool> visited(n + 1, false);

	for (int i = 0; i < edges.size(); i++)
	{
		graph[edges[i][0]].push_back(edges[i][1]);
		graph[edges[i][1]].push_back(edges[i][0]);
	}

	queue<pair<int, int>> que;
	que.push({ 1, 1 });
	visited[1] = true;

	while (!que.empty())
	{
		int node = que.front().first;
		int currentLevel = que.front().second;
		que.pop();

		if (maxLevel < currentLevel)
		{
			maxLevel = currentLevel;
			answer = 1;
		}
		else answer++;

		for (int i = 0; i < graph[node].size(); i++)
		{
			if (!visited[graph[node][i]])
			{
				visited[graph[node][i]] = true;
				que.push({ graph[node][i], currentLevel + 1 });
			}
		}
	}

	return answer;
}

03-3 ABCDE ( 13023 )

  • 위와 같은 친구 관계가 존재하는지 안하는지 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

vector<int> people[2000];
bool visited[2000];
bool ispossible;

void dfs(int node, int depth)
{
	if (depth == 4)
	{
		ispossible = true;
		return;
	}

	visited[node] = true;
	for (int i = 0; i < people[node].size(); ++i)
	{
		int next = people[node][i];

		// if(!visited[next] && ispossible == false) 이렇게 하면 좀더 최적화 된다
		if (!visited[next]) dfs(next, depth + 1);
	}
	visited[node] = false;
}

int N, M;
int main()
{
	cin >> N >> M;
	for (int i = 0; i < M; ++i)
	{
		int a, b;
		cin >> a >> b;
		people[a].push_back(b);
		people[b].push_back(a);
	}

	for (int i = 0; i < N; ++i)
	{
		dfs(i, 0);
		if (ispossible) break;
	}

	cout << ispossible << '\n';
}

03-4 연결 요소의 개수 ( 11724 )

  • 방향 없는 그래프가 주어졌을 때, 연결 요소 (Connected Component)의 개수를 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

vector<int> vect[1001];	// 인접리스트
bool visited[1001]; // 방문기록
int N, M;

/* DFS : 방문기록 남기는 용도 */
void DFS(int vertex)
{
    visited[vertex] = true;
    for (int i = 0; i < vect[vertex].size(); i++) // 최댓값 주의 : 벡터 그 원소에 해당하는 크기만큼 돌아야함
    {
        int idx = vect[vertex][i];
        if (!visited[idx])
        {
            DFS(idx);
        }
    }
}

int main()
{
    int u, v;
    int cnt = 0;
    cin >> N >> M;
    for (int i = 0; i < M; i++)
    {
        cin >> u >> v;
        vect[u].push_back(v);
        vect[v].push_back(u); // 무방향 그래프이기 때문
    }

    for (int i = 1; i <= N; i++) // 빠짐없이 탐색하기 위해
    {
        if (!visited[i])
        {
            cnt++;
            DFS(i);
        }
    }
    cout << cnt << "\n";
}

03-5 이분 그래프 ( 1707 )

  • 그래프가 입력으로 주어졌을 때, 이 그래프가 이분 그래프인지 아닌지 판별하는 프로그램을 작성하시오 (참고)
// https://hongjw1938.tistory.com/117 이분 그래프 설명

#include<bits/stdc++.h>
using namespace std;

#define RED 1
#define BLUE 2

vector<int> vect[20001];
int visited[20001];
int V, E;

void BFS(int start)
{
	queue<int> q;
	q.push(start);
	visited[start] = RED;

	while (!q.empty()) // 큐가 빌 때 까지 반복(전체)
	{
		int now = q.front();
		q.pop();

		for (int i = 0; i < vect[now].size(); i++)
		{
			int next = vect[now][i];

			if (visited[next] == 0)
			{
				q.push(next);

				if (visited[now] == RED) visited[next] = BLUE;
				else if (visited[now] == BLUE) visited[next] = RED;
			}
		}
	}
}

bool check()
{
	for (int i = 1; i <= V; i++)
	{
		for (int j = 0; j < vect[i].size(); j++)
		{
			int next = vect[i][j];

			if (visited[i] == visited[next]) return false;
		}
	}

	return true;
}

int main()
{
	int K, u, v;
	cin >> K;

	while (K--)
	{
		cin >> V >> E;
		for (int i = 0; i < E; i++)
		{
			cin >> u >> v;
			vect[u].push_back(v);
			vect[v].push_back(u);
		}

		// 빠짐없이 방문하기 위해 노드 개수만큼 BFS 돌려줘야함
		for (int i = 1; i <= V; i++)
		{
			if (visited[i] == 0)
			{
				BFS(i);
			}
		}

		// 이분그래프 검사
		if (check()) cout << "YES\n";
		else cout << "NO\n";

		memset(visited, 0, sizeof(visited));
		memset(vect, 0, sizeof(vect));
	}
}

03-6 순위 ( 프로그래머스 고득점 Kit, 그래프 )

  • 선수의 수 n, 경기 결과를 담은 2차원 배열 results가 매개변수로 주어질 때 정확하게 순위를 매길 수 있는 선수의 수를 return 하도록 solution 함수를 작성해주세요 (참고)
#include<bits/stdc++.h>
using namespace std;

int solution(int n, vector<vector<int>> results)
{
	int answer = 0;
	// (n+1) x (n+1) 크기의 2차원 boolean 그래프를 생성합니다
	vector<vector<bool>> graph(n + 1, vector<bool>(n + 1, false));

	// 결과에 따라 그래프를 갱신합니다.
	for (auto r : results)
		graph[r[0]][r[1]] = true; // 선수 r[0]가 선수 r[1]을 이겼다면 true로 표시합니다

	// Floyd-Warshall 알고리즘을 사용하여 그래프를 갱신합니다
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			for (int k = 1; k <= n; k++)
				if (graph[j][i] && graph[i][k])
					graph[j][k] = true;

	// 각 선수에 대해 결과를 계산합니다
	for (int i = 1; i <= n; i++)
	{
		int count = 0;
		for (int j = 1; j <= n; j++)
			if (graph[i][j] || graph[j][i]) count++;

		if (count == n - 1) answer++; // 선수 i의 결과가 n-1개와 일치한다면 answer를 증가시킵니다
	}

	return answer;
}

03-7 네트워크 연결 ( 1922 )

  • 이제 각 컴퓨터를 연결하는데 필요한 비용이 주어졌을 때 모든 컴퓨터를 연결하는데 필요한 최소비용을 출력하라 모든 컴퓨터를 연결할 수 없는 경우는 없다 (참고)
// https://codekodo.tistory.com/68
// 유니온 파인드를 활용해 MST 문제를 해결한다

#include<bits/stdc++.h>
using namespace std;

int N, M, res = 0;
int parent[1001];
vector<pair<int, pair<int, int>>> v;

int findParent(int a)
{
	if (parent[a] == a) return a;
	else return parent[a] = findParent(parent[a]);
}

void unionParent(int a, int b)
{
	a = findParent(a);
	b = findParent(b);

	if (a < b) parent[b] = a;
	else parent[a] = b;
}

bool sameParent(int a, int b)
{
	a = findParent(a);
	b = findParent(b);

	if (a == b) return true;
	else return false;
}

void solution()
{
	sort(v.begin(), v.end());

	for (int i = 1; i <= N; i++)
		parent[i] = i;

	for (int i = 0; i < M; i++)
	{
		int a = v[i].second.first;
		int b = v[i].second.second;
		int cost = v[i].first;

		if (!sameParent(a, b))
		{
			unionParent(a, b);
			res += cost;
		}
	}

	cout << res;
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);

	cin >> N;
	cin >> M;

	for (int i = 0; i < M; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;

		v.push_back({ c, {a, b} });
	}

	solution();

	return 0;
}


Chapter 04 DFS ( 중요도 하 )

04-1 문제

  • 설명


Chapter 05 백트래킹 ( 중요도 하 )

05-1 문제

  • 설명


Chapter 06 BFS ( 중요도 상 )

06-1 뱀과 사다리 게임 ( 16928 )

  • 게임판의 상태가 주어졌을 때, 100번 칸에 도착하기 위해 주사위를 굴려야 하는 횟수의 최솟값을 구해보자 (참고)
#include<bits/stdc++.h>
using namespace std;

const int MAX = 102;

void game(vector<int>& warp, vector<bool>& visited, int location, int c)
{
	queue<pair<int, int>> que;
	que.push(make_pair(location, c));
	visited[location] = true;

	while (!que.empty())
	{
		// 다음 위치 세팅
		int x = que.front().first;
		int cnt = que.front().second;
		que.pop();

		for (int i = 1; i <= 6; i++)
		{
			// 주사위 숫자만큼 이동
			int nx = x + i;
			int ncnt = cnt + 1;

			// 도착 했다면 정답을 출력
			if (nx == 100)
			{
				cout << ncnt << endl;
				return;
			}
			// 이 조건문이 없어도 정상 실행되는것 확인
			else if (nx < 100)
			{
				// 워프를 태울수 있을때 까지 태운다
				while (warp[nx] != 0)
				{
					nx = warp[nx];
				}

				// 아직 방문한적 없는 노드라면 큐에 넣는다
				if (!visited[nx])
				{
					que.push(make_pair(nx, ncnt));
					visited[nx] = true;
				}
			}
		}
	}
}

int main()
{
	ios_base::sync_with_stdio(false); cout.tie(NULL); cin.tie(NULL);

	vector<int> warp(MAX, 0);
	vector<bool> visited(MAX, false);

	int sadari, snake;
	cin >> sadari >> snake;

	for (int i = 0; i < sadari; i++)
	{
		int from = 0, to = 0;
		cin >> from >> to;
		warp[from] = to;
	}
	for (int i = 0; i < snake; i++)
	{
		int from = 0, to = 0;
		cin >> from >> to;
		warp[from] = to;
	}

	game(warp, visited, 1, 0);

	return 0;
}

06-2 연구소 ( 14502 )

  • 연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

int answer = 0;

int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, 1, -1 };

// 여기서는 참조자를 사용해도 된다
void BFS(vector<vector<int>>& mymap)
{
	queue<pair<int, int>> que;

	// 바이러스 최초 위치들을 모두 큐에 담는다
	for (int y = 0; y < mymap.size(); y++)
	{
		for (int x = 0; x < mymap[0].size(); x++)
		{
			if (mymap[y][x] == 2)
				que.push({ x, y });
		}
	}

	// 바이러스를 최대한 퍼뜨린다
	while (!que.empty())
	{
		int x = que.front().first;
		int y = que.front().second;
		que.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];

			if (nx < 0 || ny < 0 || mymap[0].size() <= nx || mymap.size() <= ny || mymap[ny][nx] != 0)
				continue;

			que.push({ nx, ny });
			mymap[ny][nx] = 2;
		}
	}

	// 바이러스가 모두 퍼진 상태에서 안전 영역 크기를 계산한다
	int cnt = 0;
	for (int y = 0; y < mymap.size(); y++)
	{
		for (int x = 0; x < mymap[0].size(); x++)
		{
			if (mymap[y][x] == 0)
				cnt++;
		}
	}

	answer = max(answer, cnt);
}

// 여기서는 참조자를 사용하면 안된다
void DFS(vector<vector<int>> mymap, int x, int y, int cnt)
{
	mymap[y][x] = 1;
	cnt = cnt - 1;

	// 벽을 더이상 세울수 없다면 바이러스를 퍼뜨린다
	if (cnt == 0)
	{
		BFS(mymap);

		return;
	}

	// 벽 2개를 세울수 있는 모든 경우의 수를 체크한다
	for (int y = 0; y < mymap.size(); y++)
	{
		for (int x = 0; x < mymap[0].size(); x++)
		{
			if (mymap[y][x] == 0)
				DFS(mymap, x, y, cnt);
		}
	}
}

int main()
{
	int N, M;
	cin >> N >> M;

	vector<vector<int>> mymap(N, vector<int>(M, 0));

	for (int y = 0; y < N; y++)
	{
		for (int x = 0; x < M; x++)
			cin >> mymap[y][x];
	}

	// 벽 3개를 세울수 있는 모든 경우의 수를 체크한다
	for (int y = 0; y < N; y++)
	{
		for (int x = 0; x < M; x++)
		{
			if (mymap[y][x] == 0)
				DFS(mymap, x, y, 3);
		}
	}

	cout << answer << endl;

	return 0;
}

06-3 점프 게임 ( 15558 )

  • 각 칸의 정보가 주어졌을 때, 게임을 클리어 할 수 있는지, 없는지 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

typedef struct
{
	int x, y, x_limit;
}node;

int main()
{
	vector<vector<bool>> visited(2, vector<bool>(200001, false));

	int N, K;
	cin >> N >> K;

	for (int y = 0; y < 2; y++)
	{
		for (int x = 0; x < N; x++)
		{
			char value;
			cin >> value;

			if (value == '0')
			{
				visited[y][x] = true;
			}
			else
			{
				visited[y][x] = false;
			}
		}
	}

	// 필요한 변수 초기화
	bool flag = false;
	queue<node> que;
	que.push({ 0, 0, 0 });
	visited[0][0] = true;

	while (!que.empty())
	{
		// 다음 위치 세팅
		node cur = que.front();
		int x = cur.x, y = cur.y, x_limit = cur.x_limit;
		que.pop();

		// 목적지를 넘어서면 플래그를 true로 세팅한다
		if (N <= x)
		{
			flag = true;
			break;
		}

		//전진
		if (visited[y][x + 1] == false)
		{
			visited[y][x + 1] = true;
			que.push({ x + 1, y, x_limit + 1 });
		}

		//후진 
		if (visited[y][x - 1] == false && x_limit < x - 1)
		{
			visited[y][x - 1] = true;
			que.push({ x - 1, y, x_limit + 1 });
		}

		//점프
		if (visited[1 - y][x + K] == false)
		{
			visited[1 - y][x + K] = true;
			que.push({ x + K, 1 - y, x_limit + 1 });
		}

	}

	// 플래그에 따라 결과 출력
	if (flag == false)
		cout << 0;
	else
		cout << 1;
}

06-4 4연산 ( 14395 )

  • 정수 s의 값을 t로 바꾸는 최소 연산 횟수를 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

const long long MAX = 1e9;

set<long long> visited;

int main()
{
    ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

    int start, target;
    cin >> start >> target;

    if(start == target)
    {
        cout << 0 << "\n";
        return 0;
    }
    
    // 필요한 변수 초기화
    queue<pair<long long, string>> que;
    que.push({start, ""});
    
    while (!que.empty())
    {
        // 다음 숫자 세팅
        long long n = que.front().first;
        string temp = que.front().second;
        que.pop();

        // 원하는 숫자라면 연산자 출력후 리턴
        if (n == target)
        {
            cout << temp << "\n";
            return 0;
        }

        // 현재 노드에서 방문할 수 있는 노드를 모두 큐에 담는다 ( https://twpower.github.io/92-how-to-use-set-in-cpp // count 함수에 대한 정보 )
        if (0 <= n * n && n * n <= MAX && visited.count(n*n) == 0 )
        {
            que.push({ n * n, temp + "*" });
            visited.insert(n*n);
        }
        if (0 <= n + n && n + n <= MAX && visited.count(n+n) == 0)
        {
            que.push({ n + n, temp + "+" });
            visited.insert(n + n);
        }
        if (0 <= n - n && n - n <= MAX && visited.count(n-n) == 0)
        {
            que.push({ n - n, temp + "-" });
            visited.insert(n - n);
        }
        if (n != 0 && 0 <= n / n && n / n <= MAX && visited.count(n/n) == 0)
        {
            que.push({ n / n, temp + "/" });
            visited.insert(n / n);
        }

    }
    
    // 변환이 불가능한 경우 -1을 출력한다
    cout << -1 << "\n";
    return 0;
}

06-5 물통 ( 2251 )

  • 첫 번째 물통(용량이 A인)이 비어 있을 때, 세 번째 물통(용량이 C인)에 담겨있을 수 있는 물의 양을 모두 구해내는 프로그램을 작성하시오 (참고)
// https://j2wooooo.tistory.com/130 이분은 push 이후 바로 방문처리 하게함

#include<bits/stdc++.h>
using namespace std;

typedef struct
{
	int a, b, c;
}node;

const int MAX = 201;

int CapacityA, CapacityB, CapacityC;
bool visited[MAX][MAX][MAX];

vector<int> answer;

void Input()
{
	cin >> CapacityA >> CapacityB >> CapacityC;
}

void Solution()
{
	// 필요한 변수 초기화
	queue<node> que;
	que.push({ 0, 0, CapacityC });

	while (!que.empty())
	{
		// 다음 물높이 세팅
		node cur = que.front();
		int a = cur.a;
		int b = cur.b;
		int c = cur.c;
		que.pop();

		// 해당 물높이 방문 처리
		if (visited[a][b][c] == true) continue;
		visited[a][b][c] = true;

		// 첫 번째 물통(용량이 A인)이 비어 있을 때, 세 번째 물통(용량이 C인)에 담겨있는 물의 양을 정답지에 넣는다
		if (a == 0) answer.push_back(c);

		// A물통에서 B물통으로 줄 때
		if (CapacityB < a + b) que.push({ a + b - CapacityB, CapacityB, c });
		else que.push({ 0, a + b, c });

		// A물통에서 C물통으로 줄 때
		if (CapacityC < a + c) que.push({ a + c - CapacityC, b, CapacityC });
		else que.push({ 0, b, a + c });

		// B물통에서 A물통으로 줄 때
		if (CapacityA < b + a) que.push({ CapacityA, b + a - CapacityA, c });
		else que.push({ b + a, 0, c });

		// B물통에서 C물통으로 줄 때
		if (CapacityC < b + c) que.push({ a, b + c - CapacityC, CapacityC });
		else que.push({ a, 0, b + c });

		// C물통에서 A물통으로 줄 때
		if (CapacityA < c + a) que.push({ CapacityA, b, c + a - CapacityA });
		else que.push({ c + a, b, 0 });

		// C물통에서 B물통으로 줄 때
		if (CapacityB < c + b) que.push({ a, CapacityB, c + b - CapacityB });
		else que.push({ a, c + b, 0 });
	}

	sort(answer.begin(), answer.end());

	for (int i = 0; i < answer.size(); i++)
	{
		cout << answer[i] << " ";
	}
}

void Solve()
{
	Input();
	Solution();
}

int main(void)
{
	ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

	Solve();

	return 0;
}

06-6 토마토 ( 7576 )

  • 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라 (참고)
#include <bits/stdc++.h>
using namespace std;

// 그래프 관련 변수
int n, m;
int graph[1001][1001];
queue<pair<int, int>> q;

// 상, 하, 좌, 우 이동
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };

void bfs(void)
{
	while (!q.empty())
	{
		// main에서 분명히 y 좌표를 first에 입력해놨다
		int y = q.front().first;
		int x = q.front().second;
		q.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];

			if (nx < 0 || ny < 0 || m <= nx || n <= ny) continue;
			if (graph[ny][nx] == -1) continue;

			if (graph[ny][nx] == 0)
			{
				q.push({ ny, nx });
				graph[ny][nx] = graph[y][x] + 1;
			}
		}
	}
}

int main()
{
	// 입력받기
	cin >> m >> n;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			scanf("%d", &graph[i][j]);
			if (graph[i][j] == 1)
			{
				// 분명히 y 좌표를 first에 입력해놨다
				q.push({ i, j });
			}
		}
	}

	// BFS를 통해 탐색
	bfs();

	// 안 익은 토마토 없나 확인
	int result = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			result = max(result, graph[i][j] - 1);

			if (graph[i][j] == 0)
			{
				cout << -1 << endl;
				return 0;
			}
		}
	}

	// 결과 출력
	printf("%d\n", result);
	return 0;
}

06-7 경쟁적 전염 ( 18405 )

  • 시험관의 크기와 바이러스의 위치 정보가 주어졌을 때, S초가 지난 후에 (X,Y)에 존재하는 바이러스의 종류를 출력하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

const int MAX = 202;

int arr[MAX][MAX];
int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, 1, -1 };

struct Virus
{
	int virus;
	int x;
	int y;
};

bool cmp(const Virus& v1, const Virus& v2)
{
	return v1.virus < v2.virus;
}

int main()
{
	vector<Virus> vec;

	int N, K;
	cin >> N >> K;

	for (int y = 0; y < N; y++)
	{
		for (int x = 0; x < N; x++)
		{
			cin >> arr[y][x];
			vec.push_back({ arr[y][x], x, y });
		}
	}

	int S, X, Y;
	cin >> S >> Y >> X;

	int s = 0;
	sort(vec.begin(), vec.end(), cmp);

	while (s < S)
	{
		int len = vec.size();
		for (int j = 0; j < len; j++)
		{
			Virus v = vec[j];
			for (int i = 0; i < 4; i++)
			{
				int nx = v.x + dx[i];
				int ny = v.y + dy[i];

				if (nx < 0 || ny < 0 || N <= nx || N <= ny) continue;

				if (arr[ny][nx] == 0)
				{
					arr[ny][nx] = v.virus;
					vec.push_back({ arr[ny][nx], nx, ny });
				}
			}
		}

		if (0 < arr[Y - 1][X - 1]) break;
		s++;
	}

	cout << arr[Y - 1][X - 1];

	return 0;
}

06-8 숨바꼭질 4 ( 13913 )

  • 수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;
#define max 1000001

int N, K;
int parent[max];
bool visited[max];

// 현재 위치와 걸린 시간
struct node
{
	int cur;
	int time;
};

void bfs(int start)
{
	queue<node> q;
	q.push({ start, 0 });
	visited[start] = true;

	while (!q.empty())
	{
		int cur = q.front().cur;
		int time = q.front().time;
		q.pop();

		// 목적지 도착 하면 종료
		if (cur == K)
		{
			cout << time << endl;
			return;
		}

		// 해당 지점을 처음 방문하는 경우 부모 설정 및 방문 처리
		if (0 <= cur - 1)
		{
			if (!visited[cur - 1])
			{
				q.push({ cur - 1, time + 1 });
				visited[cur - 1] = true;
				parent[cur - 1] = cur;
			}
		}
		if (cur + 1 < max)
		{
			if (!visited[cur + 1])
			{
				q.push({ cur + 1, time + 1 });
				visited[cur + 1] = true;
				parent[cur + 1] = cur;
			}
		}
		if (cur * 2 < max)
		{
			if (!visited[cur * 2])
			{
				q.push({ cur * 2, time + 1 });
				visited[cur * 2] = true;
				parent[cur * 2] = cur;
			}
		}
	}
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);

	cin >> N >> K;
	bfs(N);

	stack<int> stk;

	stk.push(K);
	int cur = K;
	while (cur != N)
	{
		stk.push(parent[cur]);
		cur = stk.top();
	}

	while (!stk.empty())
	{
		cout << stk.top() << ' ';
		stk.pop();
	}

	return 0;
}

06-9 이모티콘 ( 14226 )

  • 영선이가 S개의 이모티콘을 화면에 만드는데 걸리는 시간의 최솟값을 구하는 프로그램을 작성하시오 (참고)
// https://junbastick.tistory.com/17 참고
#include<iostream>
#include<vector>
#include<queue>

#define endl "\n"
using namespace std;

#define MAX 1001
bool visit[MAX][MAX];

int BFS(int S)
{
	queue<pair<pair<int, int>, int>> q;
	q.push(make_pair(make_pair(1, 0), 0));
	visit[1][0] = true;

	while (!q.empty())
	{
		int screen = q.front().first.first;
		int clip = q.front().first.second;
		int time = q.front().second;
		q.pop();

		// 각 시간대 별로 현재 상황을 사진 찍어놓는다는 느낌
		if (screen == S)
			return time;
		if (!visit[screen][screen])
		{
			q.push(make_pair(make_pair(screen, screen), time + 1));
			visit[screen][screen] = true;
		}
		if (screen + clip <= S && !visit[screen + clip][clip])
		{
			q.push(make_pair(make_pair(screen + clip, clip), time + 1));
			visit[screen + clip][clip] = true;
		}
		if (2 <= screen - 1 && !visit[screen - 1][clip])
		{
			q.push(make_pair(make_pair(screen - 1, clip), time + 1));
			visit[screen - 1][clip] = true;
		}
	}
}

void Answer()
{
	int S;
	cin >> S;
	cout << BFS(S) << endl;
}

int main()
{
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
	Answer();
	return 0;
}
#include <bits/stdc++.h>
using namespace std;

//1개는 이미 입력
int S;

int visited[3000][3000];
int main()
{
	cin >> S;
	//현재 상태, 클립보드에 저장된 개수

	queue<pair<int, int>> q;
	q.push({ 1,0 });
	visited[1][0] = 1;

	while (!q.empty())
	{
		auto cur = q.front();
		q.pop();
		int now = cur.first;
		int clip = cur.second;

		//체크
		if (now == S)
		{
			cout << visited[now][clip] - 1 << '\n';
			break;
		}

		//1.복사
		int nextClip = now;
		if (visited[now][nextClip] == 0)
		{
			q.push({ now, nextClip });
			visited[now][nextClip] = visited[now][clip] + 1;
		}

		//2.붙여넣기
		if (now + clip <= S)
		{
			if (visited[now + clip][clip] == 0)
			{
				q.push({ now + clip, clip });
				visited[now + clip][clip] = visited[now][clip] + 1;
			}
		}

		//3.삭제
		if (now > 0)
		{
			if (visited[now - 1][clip] == 0)
			{
				q.push({ now - 1, clip });
				visited[now - 1][clip] = visited[now][clip] + 1;
			}
		}
	}

	return 0;
}

06-10 알고스팟 ( 1261 )

  • 현재 (1, 1)에 있는 알고스팟 운영진이 (N, M)으로 이동하려면 벽을 최소 몇 개 부수어야 하는지 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

#define MAX 1e9

int N, M;
int board[101][101];
int dist[101][101];
int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };

void bfs()
{
	queue<pair<int, int>> q;
	q.push({ 0,0 });
	dist[0][0] = 0;

	while (!q.empty())
	{
		int x = q.front().first;
		int y = q.front().second;
		q.pop();

		for (int i = 0; i < 4; i++)
		{
			int nx = x + dx[i];
			int ny = y + dy[i];

			if (nx < 0 || ny < 0 || M <= nx || N <= ny) continue;

			if (board[ny][nx] == 0)
			{
				// 이 경우가 성립한다면 벽을 부술 필요가
				// 없는데 부순애가 지나갔다는 의미
				if (dist[y][x] < dist[ny][nx])
				{
					dist[ny][nx] = dist[y][x];
					q.push({ nx,ny });
				}
			}
			else if (board[ny][nx] == 1)
			{
				// 이 경우가 성립하다면 벽을 더 적게
				// 부수고 지나갈수 있다는 의미
				if (dist[y][x] + 1 < dist[ny][nx])
				{
					dist[ny][nx] = dist[y][x] + 1;
					q.push({ nx,ny });
				}
			}
		}
	}
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> M >> N;
	string str;

	for (int i = 0; i < N; i++)
	{
		cin >> str;
		for (int j = 0; j < M; j++)
		{
			dist[i][j] = MAX;
			board[i][j] = str[j] - '0';
		}
	}

	bfs();
	cout << dist[N - 1][M - 1] << "\n";

	return 0;
}


Chapter 07 완전탐색 ( 중요도 상 )

07-1 테트로미노 ( 14500 )

  • 첫째 줄에 테트로미노가 놓인 칸에 쓰인 수들의 합의 최댓값을 출력한다
// https://9967han.tistory.com/15 참고

#include<bits/stdc++.h>
using namespace std;

const int MAX = 501;

int dx[] = { 1, -1, 0, 0 };
int dy[] = { 0, 0, 1, -1 };
int N, M, ans = 0;

void getMaxNum(vector<vector<int>>& mymap, vector<vector<bool>>& visited, int x, int y, int cnt, int curScore)
{
	// 블록이 4개라면 스코어 갱신이 가능한지 체크
	if (cnt == 4)
	{
		if (ans < curScore) ans = curScore;
		return;
	}

	// 현재 노드에서 만들수 있는 모든 도형을 만들자
	for (int i = 0; i < 4; ++i)
	{
		int nx = x + dx[i];
		int ny = y + dy[i];

		if (nx < 0 || ny < 0 || M <= nx || N <= ny || visited[ny][nx]) continue;

		visited[ny][nx] = true;
		getMaxNum(mymap, visited, nx, ny, cnt + 1, curScore + mymap[ny][nx]);
		visited[ny][nx] = false;
	}
}

int main()
{
	vector<vector<int>> mymap(MAX, vector<int>(MAX, 0));
	vector<vector<bool>> visited(MAX, vector<bool>(MAX, false));

	cin >> N >> M;

	for (int y = 0; y < N; ++y)
	{
		for (int x = 0; x < M; ++x)
		{
			cin >> mymap[y][x];
		}
	}

	for (int y = 0; y < N; ++y)
	{
		for (int x = 0; x < M; ++x)
		{
			// 모든 노드를 방문한다
			visited[y][x] = true;
			getMaxNum(mymap, visited, x, y, 1, mymap[y][x]);
			visited[y][x] = false;

			// ㅏ ㅓ ㅗ ㅜ 를 만들자
			if (0 <= y - 1 && x + 1 < M && y + 1 < N) //ㅏ
			{
				ans = max(ans, (mymap[y - 1][x] + mymap[y][x] + mymap[y][x + 1] + mymap[y + 1][x]));
			}
			if (0 <= y - 1 && 0 <= x - 1 && y + 1 < N) //ㅓ
			{
				ans = max(ans, (mymap[y - 1][x] + mymap[y][x] + mymap[y][x - 1] + mymap[y + 1][x]));
			}
			if (0 <= y - 1 && 0 <= x - 1 && x + 1 < M) //ㅗ
			{
				ans = max(ans, (mymap[y - 1][x] + mymap[y][x - 1] + mymap[y][x] + mymap[y][x + 1]));
			}
			if (0 <= x - 1 && x + 1 < M && y + 1 < N) //ㅜ
			{
				ans = max(ans, (mymap[y][x - 1] + mymap[y][x] + mymap[y][x + 1] + mymap[y + 1][x]));
			}
		}
	}

	cout << ans;
}

07-2 감시 ( 15683 )

  • 첫째 줄에 사각 지대의 최소 크기를 출력한다
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int N, M;
int arr[8][8];
vector<pair<int, int>> cctv; // CCTV의 위치를 저장할 벡터
int ans = 987654321; // 최소로 막아야 할 칸의 수

int dy[4] = { 0, -1, 0, 1 }; // 방향에 따른 y좌표 변화량
int dx[4] = { 1, 0, -1, 0 }; // 방향에 따른 x좌표 변화량

// CCTV가 볼 수 있는 영역을 체크하는 함수
void check(int x, int y, int dir)
{
	dir %= 4; // dir이 4보다 큰 경우를 위해 나머지 연산으로 방향을 구함
	while (1)
	{
		int nx = x + dx[dir]; // 현재 위치에서 방향에 따라 이동한 x좌표
		int ny = y + dy[dir]; // 현재 위치에서 방향에 따라 이동한 y좌표

		x = nx; // 다음 위치로 x좌표 갱신
		y = ny; // 다음 위치로 y좌표 갱신

		if (nx < 0 || ny < 0 || M <= nx || N <= ny) return; // 범위를 벗어난 경우 반환
		if (arr[ny][nx] == 6) return; // 벽인 경우 반환
		if (arr[ny][nx] != 0) continue; // 이미 CCTV가 있거나, 이미 감시되는 영역인 경우 넘어감

		arr[ny][nx] = -1; // CCTV로부터 감시되는 영역이므로 -1로 표시
	}
}

// 모든 CCTV를 처리하는 dfs 함수
void dfs(int idx)
{
	// 모든 CCTV를 처리한 경우
	if (idx == cctv.size())
	{
		int cnt = 0; // 막아야 할 칸의 수를 세기 위한 변수
		for (int i = 0; i < N; i++)
			for (int j = 0; j < M; j++)
				if (!arr[i][j]) cnt++; // CCTV로부터 감시되지 않는 영역의 개수를 센다

		ans = min(ans, cnt); // 최소값 갱신
		return;
	}

	int y = cctv[idx].first;
	int x = cctv[idx].second;
	for (int dir = 0; dir < 4; dir++)
	{
		int tmp[8][8];
		for (int i = 0; i < N; i++)
			for (int j = 0; j < M; j++)
				tmp[i][j] = arr[i][j];

		if (arr[y][x] == 1)
			check(x, y, dir);
		else if (arr[y][x] == 2)
		{
			check(x, y, dir);
			check(x, y, dir + 2);
		}
		else if (arr[y][x] == 3)
		{
			check(x, y, dir);
			check(x, y, dir + 1);
		}
		else if (arr[y][x] == 4)
		{
			check(x, y, dir);
			check(x, y, dir + 1);
			check(x, y, dir + 2);
		}
		else if (arr[y][x] == 5)
		{
			check(x, y, dir);
			check(x, y, dir + 1);
			check(x, y, dir + 2);
			check(x, y, dir + 3);
		}

		dfs(idx + 1);

		// case 종료이므로 초기화
		for (int i = 0; i < N; i++)
			for (int j = 0; j < M; j++)
				arr[i][j] = tmp[i][j];
	}
}

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

	cin >> N >> M;

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cin >> arr[i][j];
			if (arr[i][j] != 0 && arr[i][j] != 6)
				cctv.push_back({ i, j });
		}
	}

	dfs(0);
	cout << ans << '\n';

	return 0;
}

07-3 치킨 배달 ( 15686 )

  • 첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력한다 (참고)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX = 50;
const int INF = 987654321;

int N, M;
int result = INF;
int graph[MAX][MAX]; // 치킨 거리를 계산하기 위한 그래프
vector<pair<int, int>> house, chicken; // 집과 치킨집 위치를 저장하는 벡터
bool live[13]; // 치킨집을 선택했는지 여부를 저장하는 배열

int distance(pair<int, int> a, pair<int, int> b) // 두 위치 사이의 거리를 계산하는 함수
{
	return abs(a.first - b.first) + abs(a.second - b.second);
}

void DFS(int idx, int selected) // 치킨집을 선택하는 모든 경우의 수를 탐색하는 함수
{
	if (selected == M) // 선택한 치킨집의 개수가 M개일 경우
	{
		int tempResult = 0;
		for (int i = 0; i < house.size(); i++) // 모든 집에 대해
		{
			int dist = INF;
			for (int j = 0; j < chicken.size(); j++) // 선택한 치킨집 중 가장 가까운 거리를 찾음
			{
				if (live[j])
					dist = min(dist, distance(house[i], chicken[j]));
			}
			tempResult = tempResult + dist; // 가장 가까운 거리를 더해줌
		}
		result = min(result, tempResult); // 최소값을 갱신

		return;
	}

	if (idx == chicken.size()) // 탐색이 끝났을 경우
		return;

	//프랜차이즈 선정
	live[idx] = true; // 현재 치킨집을 선택함
	DFS(idx + 1, selected + 1); // 다음 치킨집을 선택하기 위해 DFS 호출
	//프랜차이즈 선정 X
	live[idx] = false; // 현재 치킨집을 선택하지 않음
	DFS(idx + 1, selected); // 다음 치킨집을 선택하기 위해 DFS 호출
}

int main(void)
{
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cin >> N >> M;

	for (int i = 0; i < N; i++) // 입력받은 그래프를 저장하고, 집과 치킨집의 위치를 벡터에 저장
		for (int j = 0; j < N; j++)
		{
			cin >> graph[i][j];
			if (graph[i][j] == 1)
				house.push_back({ i, j });
			else if (graph[i][j] == 2)
				chicken.push_back({ i, j });
		}

	DFS(0, 0); // 치킨집 선택을 위한 DFS 호출

	cout << result << "\n"; // 최소 치킨 거리 출력

	return 0;
}

07-4 야구 ( 17281 )

  • 아인타팀이 얻을 수 있는 최대 점수를 출력한다 (참고)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

int main(void)
{
	// 선수 번호는 0 ~ 8로 하여 배열 접근이 쉽게 한다.
	int N;
	vector<vector<int>> score(50, vector<int>(9)); // 각 이닝의 선수별 타격 결과를 저장하는 벡터
	int ans = 0; // 최대 점수를 저장할 변수

	vector<int> player = { 1, 2, 3, 4, 5, 6, 7, 8 }; // 타자 순서를 저장하는 벡터

	cin >> N; // 이닝의 수를 입력 받음

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			cin >> score[i][j]; // 각 이닝의 각 선수의 결과를 입력 받음
		}
	}

	do
	{
		int sum = 0; // 현재 순서로 게임을 진행했을 때의 총 점수
		int index = 0; // 현재 타자의 인덱스
		vector<int> tmp_player = player; // 현재 순서로 타자를 저장할 벡터

		// 아인타는 자신이 가장 좋아하는 선수인 1번 선수를 4번 타자로 미리 결정했다
		// player 선언한 부분을 보면 8명의 선수밖에 등록 안되어 있는것을 확인할 수 있다
		tmp_player.insert(tmp_player.begin() + 3, 0);

		for (int inning = 0; inning < N; inning++)
		{
			queue<int> base; // 베이스에 있는 주자를 관리하는 큐

			int out = 0; // 현재 이닝의 아웃 수
			while (out != 3) // 아웃 수가 3이 될 때까지 반복
			{
				// 각 이닝 당 타순에 맞게 결과를 저장.
				int state = score[inning][tmp_player[index++]];

				if (state == 0)
				{
					out++; // 아웃
				}
				else if (state == 1)
				{
					// 안타
					// 큐가 비어있으면 1루에 주자 1명을 두고, 2, 3루는 비어둔다.
					// 큐가 비어있지 않으면 큐의 사이즈가 3인지 검사.
					// 사이즈가 3인 경우 베이스를 1루씩 앞당기면서 베이스의 선수 여부에 따라 점수를 더해준다.
					// 사이즈가 3이 아닌 경우 1을 푸시하여 1루에 진루.
					if (base.empty())
					{
						base.push(0);
						base.push(0);
						base.push(1);
					}
					else
					{
						if (base.size() == 3)
						{
							sum += base.front();
							base.pop();
						}
						base.push(1);
					}
				}
				else if (state == 2)
				{
					// 2루타
					// 큐가 비어있으면 2루에 주자 1명을 두고, 1, 3루는 비어둔다.
					// 큐가 비어있지 않으면 큐의 사이즈가 3인지 검사.
					// 사이즈가 3인 경우 베이스를 2루씩 앞당기면서 베이스의 선수 여부에 따라 점수를 더해준다.
					// 사이즈가 3이 아닌 경우 1과 0을 푸시하여 2루에 선수 1명을 진루시키고, 1루에는 주자가 없다.
					if (base.empty())
					{
						base.push(0);
						base.push(1);
						base.push(0);
					}
					else
					{
						if (base.size() == 3)
						{
							for (int i = 0; i < 2; i++)
							{
								sum += base.front();
								base.pop();
							}
						}
						base.push(1);
						base.push(0);
					}
				}
				else if (state == 3)
				{
					// 3루타
					// 큐가 비어있으면 3루에 주자 1명을 두고, 1, 2루는 비어둔다.
					// 큐가 비어있지 않으면 사이즈가 3인지 검사.
					// 사이즈가 3인 경우 베이스를 3루씩 앞당기면서 베이스의 선수 여부에 따라 점수를 더해준다.
					// 사이즈가 3이 아닌 경우 1, 0, 0을 푸시하여 3루에 선수 1명 진루, 1, 2루에는 주자가 없다.
					if (base.empty())
					{
						base.push(1);
						base.push(0);
						base.push(0);
					}
					else
					{
						if (base.size() == 3)
						{
							for (int i = 0; i < 3; i++)
							{
								sum += base.front();
								base.pop();
							}
						}
						base.push(1);
						base.push(0);
						base.push(0);
					}
				}
				else if (state == 4)
				{
					// 홈런
					// 모든 주자가 들어와야 하기 때문에 큐가 빌 때까지 베이스의 선수 여부에 따라 점수를 더해준다.
					// 큐가 비게되면 홈런을 친 선수의 점수까지 더해주는 ++을 진행.
					while (!base.empty())
					{
						sum += base.front();
						base.pop();
					}
					sum++;
				}

				if (index == 9) index = 0;
			}
		}
		//모든 타순을 다르게 하여 최대값을 얻는 점수를 저장.
		ans = ans < sum ? sum : ans;

	} while (next_permutation(player.begin(), player.end()));

	cout << ans;

	return 0;
}
// https://codingnotes.tistory.com/113 킵 해두자

07-5 배열 돌리기 4 ( 17406 )

  • 배열 A의 값의 최솟값을 출력한다 (참고)
#include <iostream>
#include <algorithm> // min
#include <vector>
using namespace std;

struct Roll
{
	int row, col, size;
};

int N, M, K;
int grid[51][51];
int copiedGrid[51][51]; // Copy of the grid for simulation
vector<Roll> operations;
int order[6];       // Order of rotation operations
bool isChosen[6];   // Indicates if a rotation operation is chosen
int minValue = 987654321;

void performRoll(int row, int col, int size)
{
	for (int d = 1; d <= size; d++)
	{
		int start_x = col - d;
		int start_y = row - d;
		int end_x = col + d;
		int end_y = row + d;

		int tmp = copiedGrid[start_y][start_x];

		// Left side
		for (int i = start_y; i < end_y; i++)
			copiedGrid[i][start_x] = copiedGrid[i + 1][start_x];

		// Bottom side
		for (int j = start_x; j < end_x; j++)
			copiedGrid[end_y][j] = copiedGrid[end_y][j + 1];

		// Right side
		for (int i = end_y; start_y < i; i--)
			copiedGrid[i][end_x] = copiedGrid[i - 1][end_x];

		// Top side
		for (int j = end_x; start_x < j; j--)
			copiedGrid[start_y][j] = copiedGrid[start_y][j - 1];

		copiedGrid[start_y][start_x + 1] = tmp;
	}
}

void simulate()
{
	int result = 0;
	for (int i = 1; i <= N; i++)
		for (int j = 1; j <= M; j++)
			copiedGrid[i][j] = grid[i][j];

	// Perform rotation operations in the specified order
	for (int i = 0; i < K; i++)
	{
		int operationIdx = order[i];
		int row = operations[operationIdx].row;
		int col = operations[operationIdx].col;
		int size = operations[operationIdx].size;
		performRoll(row, col, size);
	}

	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= M; j++)
			result += copiedGrid[i][j];

		minValue = min(minValue, result);
		result = 0;
	}
}

void dfs(int count)
{
	if (count == K)
	{
		simulate();
		return;
	}

	for (int i = 0; i < K; i++)
	{
		if (isChosen[i]) continue;

		order[count] = i;
		isChosen[i] = true;
		dfs(count + 1);
		isChosen[i] = false;
	}
}

void solve()
{
	cin >> N >> M >> K;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= M; j++)
			cin >> grid[i][j];
	}

	for (int i = 0; i < K; i++)
	{
		int row, col, size;
		cin >> row >> col >> size;
		operations.push_back({ row, col, size });
	}

	dfs(0);
	cout << minValue << '\n';
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	solve();
	return 0;
}
// https://11001.tistory.com/30
// https://jaimemin.tistory.com/1482

07-6 테트리스 ( 3019 )

  • 블록을 놓는 서로 다른 방법의 수를 구하는 프로그램을 작성하시오 (참고)
// 강좌 - 알고리즘 중급 - 2/3
// https://www.acmicpc.net/problem/3019
// https://hsdevelopment.tistory.com/506 참고

#include <bits/stdc++.h>
using namespace std;

const int MAX = 105;

int board[MAX], c, p;
vector<string> v;

int simulation(int number)
{
	int ret = 0;

	if (number == 1)
	{
		v.push_back("0");
		v.push_back("0000");
	}
	else if (number == 2)
	{
		v.push_back("00");
	}
	else if (number == 3)
	{
		v.push_back("001");
		v.push_back("10");
	}
	else if (number == 4)
	{
		v.push_back("100");
		v.push_back("01");
	}
	else if (number == 5)
	{
		v.push_back("000");
		v.push_back("01");
		v.push_back("101");
		v.push_back("10");
	}
	else if (number == 6)
	{
		v.push_back("000");
		v.push_back("00");
		v.push_back("011");
		v.push_back("20");
	}
	else if (number == 7)
	{
		v.push_back("000");
		v.push_back("02");
		v.push_back("110");
		v.push_back("00");
	}

	for (int i = 0; i < v.size(); i++)
	{
		int len = v[i].length(); // 현재 도형의 길이를 구합니다

		vector<int> temp;
		for (auto strnum : v[i])
		{
			temp.push_back(strnum - '0'); // 현재 도형의 각 자리 숫자를 temp 벡터에 저장합니다
		}

		for (int j = 0; j + len - 1 < c; j++)
		{
			bool calc = true;
			for (int k = j; k < j + len - 1; k++)
			{
				// board의 모든 구역에 temp를 맞춰봐야 하니까 board수식과 temp 수식이 다르다
				// 현재 도형과 게임 보드의 숫자 차이를 비교하여 일치하는지 확인합니다
				if (board[k] - board[k + 1] != temp[k - j] - temp[k - j + 1])
				{
					calc = false;
					break;
				}
			}
			if (calc)
			{
				++ret; // 현재 도형과 일치하는 부분을 찾았으므로 ret 값을 증가시킵니다
			}
		}
	}

	return ret;
}

int main()
{
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

	cin >> c >> p;

	for (int i = 0; i < c; i++)
	{
		cin >> board[i];
	}

	cout << simulation(p) << endl;

	return 0;
}

07-7 직사각형으로 나누기 ( 1451 )

  • 입력으로 주어진 직사각형을 3개의 작은 직사각형으로 나누었을 때, 각각의 작은 직사각형의 합의 곱을 최대로 하는 프로그램을 작성하시오 (참고)
// 강좌 - 없음
// https://www.acmicpc.net/problem/1451

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

int N, M;
int Numbers[101][101];
long long Max = -1;

int getSum(int startY, int startX, int endY, int endX)
{
	int sum = 0;
	for (int i = startY; i <= endY; i++)
	{
		for (int j = startX; j <= endX; j++)
		{
			sum += Numbers[i][j];
		}
	}

	return sum;
}

void solution()
{
	cin >> N >> M;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			scanf("%1d", &Numbers[i][j]);
		}
	}

	// 1번 모양
	for (int i = 0; i < N - 2; i++)
	{
		for (int j = i + 1; j < N - 1; j++)
		{
			long long square1 = getSum(0, 0, i, M - 1);
			long long square2 = getSum(i + 1, 0, j, M - 1);
			long long square3 = getSum(j + 1, 0, N - 1, M - 1);
			Max = max(Max, square1 * square2 * square3);
		}
	}

	// 2번 모양
	for (int i = 0; i < M - 2; i++)
	{
		for (int j = i + 1; j < M - 1; j++)
		{
			long long square1 = getSum(0, 0, N - 1, i);
			long long square2 = getSum(0, i + 1, N - 1, j);
			long long square3 = getSum(0, j + 1, N - 1, M - 1);
			Max = max(Max, square1 * square2 * square3);
		}
	}

	// 3번 모양 ( 늘어나는 직사각형을 가장 우선시 한다 )
	for (int i = M - 1; 0 < i; i--)
	{
		for (int j = 0; j < N - 1; j++)
		{
			long long square1 = getSum(0, i, N - 1, M - 1);
			long long square2 = getSum(0, 0, j, i - 1);
			long long square3 = getSum(j + 1, 0, N - 1, i - 1);
			Max = max(Max, square1 * square2 * square3);
		}
	}

	// 4번 모양 ( 늘어나는 직사각형을 가장 우선시 한다 )
	for (int i = 0; i < M - 1; i++)
	{
		for (int j = 0; j < N - 1; j++)
		{
			long long square1 = getSum(0, 0, N - 1, i);
			long long square2 = getSum(0, i + 1, j, M - 1);
			long long square3 = getSum(j + 1, i + 1, N - 1, M - 1);
			Max = max(Max, square1 * square2 * square3);
		}
	}

	// 5번 모양 ( 늘어나는 직사각형을 가장 우선시 한다 )
	for (int i = 0; i < N - 1; i++)
	{
		for (int j = 0; j < M - 1; j++)
		{
			long long square1 = getSum(0, 0, i, M - 1);
			long long square2 = getSum(i + 1, 0, N - 1, j);
			long long square3 = getSum(i + 1, j + 1, N - 1, M - 1);
			Max = max(Max, square1 * square2 * square3);
		}
	}

	// 6번 모양 ( 늘어나는 직사각형을 가장 우선시 한다 )
	for (int i = N - 1; 0 < i; i--)
	{
		for (int j = 0; j < M - 1; j++)
		{
			long long square1 = getSum(i, 0, N - 1, M - 1);
			long long square2 = getSum(0, 0, i - 1, j);
			long long square3 = getSum(0, j + 1, i - 1, M - 1);
			Max = max(Max, square1 * square2 * square3);
		}
	}

	cout << Max << endl;
}

int main()
{
	solution();
	return 0;
}
// https://nim-code.tistory.com/89
// https://dbstndi6316.tistory.com/116

07-8 십자가 2개 놓기 ( 17085 )

  • 첫째 줄에 놓은 십자가 넓이의 곱의 최댓값을 출력한다 (참고)
// 강좌 - 알고리즘 중급 - 2/3

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

int n, m;
char map[16][16];
int dx[4] = { 1, -1, 0, 0 };
int dy[4] = { 0, 0, 1, -1 };

bool visited[16][16];
int chk[16][16];

typedef long long ll;
ll ans = 0;

bool checkRange(int x, int y)
{
	if (x < 0 || m <= x || y < 0 || n <= y)
		return false;

	return true;
}

void dfs(int cnt, ll res)
{
	if (cnt == 2)
	{
		// 최대 너비를 계산
		ans = ans < res ? res : ans;
		return;
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (chk[i][j] && !visited[i][j])
			{
				// 현재 위치를 방문했음을 표시하고, 방문 배열을 복사
				bool c_visited[16][16];
				memcpy(c_visited, visited, sizeof(visited));

				visited[i][j] = true;
				int sum = 1;

				int limit = chk[i][j] - 1;
				int idx = 1;

				// limit이 0일 경우 십자가의 넓이는 1이다
				if (!limit) dfs(cnt + 1, res * sum);

				while (limit--)
				{
					bool state = true;
					for (int d = 0; d < 4; d++)
					{
						int ny = i + dy[d] * idx;
						int nx = j + dx[d] * idx;

						if (checkRange(nx, ny) && !visited[ny][nx])
						{
							visited[ny][nx] = true;
							sum++;
						}
						else
						{
							state = false;
							break;
						}
					}

					if (state)
					{
						dfs(cnt + 1, res * sum);
						idx++;
					}
					else break;
				}
				// 방문 배열을 이전 상태로 되돌림
				memcpy(visited, c_visited, sizeof(visited));
			}
		}
	}
}

int main()
{
	cin >> n >> m;

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			cin >> map[i][j];
		}
	}

	// 각 위치에서 십자가의 최대 크기를 구함
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (map[i][j] == '#')
			{
				int idx = 1;
				bool state = true;
				while (state)
				{
					for (int d = 0; d < 4; d++)
					{
						int ny = i + dy[d] * idx;
						int nx = j + dx[d] * idx;

						if (!checkRange(nx, ny) || map[ny][nx] != '#')
						{
							state = false;
							break;
						}
					}
					if (state) idx++;
				}
				chk[i][j] = idx;
			}
		}
	}

	dfs(0, 1);
	cout << ans << '\n';
	return 0;
}
// 강좌 - 알고리즘 중급 - 2/3
// https://www.acmicpc.net/problem/17085

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll rows, cols, ans;
string grid[20];

ll findLargestCross(ll x, ll y, ll depth)
{
	ll ret = 0;
	for (int i = 0; i <= 7; i++) // 십자가 길이에 대한 반복문
	{
		ll valid = 1;
		for (int j = -i; j <= i; j++) // 배치 가능 여부 확인
			if (x + j < 0 || rows <= x + j || y + j < 0 || cols <= y + j || grid[x + j][y] != '#' || grid[x][y + j] != '#')
				valid = 0;

		if (valid)
		{
			for (int j = -i; j <= i; j++)
			{
				grid[x + j][y] = '.';
				grid[x][y + j] = '.';
			}

			if (depth) // 두 번째 십자가인 경우 바로 반환
				ret = i * 4 + 1;
			else // 첫 번째 십자가인 경우 다시 탐색
			{
				for (int j = 0; j < rows; j++)
					for (int k = 0; k < cols; k++)
						ret = max(ret, (i * 4 + 1) * findLargestCross(j, k, 1));
			}

			for (int j = -i; j <= i; j++)
			{
				grid[x + j][y] = '#';
				grid[x][y + j] = '#';
			}
		}
		else
			break;
	}

	return ret;
}

int main()
{
	cin >> rows >> cols;
	for (int i = 0; i < rows; i++)
		cin >> grid[i];

	for (int i = 0; i < rows; i++)
		for (int j = 0; j < cols; j++)
			ans = max(ans, findLargestCross(i, j, 0));

	cout << ans;
	return 0;
}
// https://jayrightthere.tistory.com/entry/BOJ-17085-%EC%8B%AD%EC%9E%90%EA%B0%80-2%EA%B0%9C-%EB%86%93%EA%B8%B0
// https://nanyoungkim.tistory.com/184
// https://he1fire.tistory.com/108

07-9 스도쿠 ( 2580 )

  • 게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오 (참고)
// 강좌 - 코딩 테스트 준비 - 연습

#include <iostream>
using namespace std;

int emptyCellCount = 0; // 빈 셀의 개수를 저장하는 변수
int sudoku[9][9]; // 스도쿠 보드를 나타내는 2차원 배열
int emptyCells[81][2]; // 빈 셀의 좌표를 저장하는 배열

// 백트래킹 함수
bool backtracking(int k)
{
	if (k == emptyCellCount) return true; // 모든 빈 셀에 대한 수를 다 채웠을 경우 1을 반환

	int y = emptyCells[k][0], x = emptyCells[k][1]; // 현재 채워야 할 빈 셀의 좌표
	int startX = (x / 3) * 3, startY = (y / 3) * 3; // 현재 채워야 할 빈 셀이 속한 3x3 영역의 시작 좌표
	bool check[10] = { false }; // 현재 채워야 할 빈 셀에서 가능한 숫자를 체크하는 배열
	bool result = false; // 백트래킹 결과를 저장하는 변수

	// 현재 행과 열에 이미 사용된 숫자를 체크
	for (int i = 0; i < 9; i++)
		check[sudoku[y][i]] = check[sudoku[i][x]] = true;

	// 현재 3x3 영역에 이미 사용된 숫자를 체크
	for (int i = startY; i < startY + 3; i++)
		for (int j = startX; j < startX + 3; j++)
			check[sudoku[i][j]] = true;

	// 가능한 숫자를 하나씩 시도하면서 백트래킹 수행
	for (int i = 1; i <= 9; i++)
	{
		if (check[i] == false) // 숫자 i가 사용되지 않았을 경우
		{
			sudoku[y][x] = i; // 숫자 i를 현재 채워야 할 빈 셀에 대입
			result = backtracking(k + 1); // 다음 빈 셀로 넘어가기 위해 백트래킹 수행
			if (result) break; // 백트래킹 결과가 true인 경우 (스도쿠가 완성된 경우) 루프 종료
			sudoku[y][x] = 0; // 백트래킹 결과가 false인 경우 (스도쿠가 완성되지 않은 경우) 숫자 i를 제거하고 다른 숫자 시도
		}
	}

	return result; // 백트래킹 결과 반환
}

int main()
{
	// 스도쿠 보드 입력 받기
	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			cin >> sudoku[i][j];
			if (sudoku[i][j] == 0)
			{
				emptyCells[emptyCellCount][0] = i; // 빈 셀의 행 좌표를 저장
				emptyCells[emptyCellCount][1] = j; // 빈 셀의 열 좌표를 저장
				emptyCellCount++; // 빈 셀 개수 증가
			}
		}
	}

	backtracking(0);

	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++)
			cout << sudoku[i][j] << " ";
		cout << "\n";
	}
}

07-10 N-Queen ( 9663 )

  • N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오 (참고)
// 강좌 - 코딩 테스트 준비 - 연습

#include<iostream>
#include<cmath>

#define endl "\n"
#define MAX 15
using namespace std;

int N, Answer;
int board[MAX];

void Input()
{
	cin >> N;  // N 입력 받기
}

bool CanPlaceQueen(int row)
{
	for (int i = 0; i < row; i++)
	{
		if (board[i] == board[row] || abs(board[row] - board[i]) == row - i)
			return false;  // 같은 열이거나 대각선에 위치한 퀸이 있으면 놓을 수 없음
	}

	return true;  // 놓을 수 있는 자리
}

void DFS(int row)
{
	if (row == N)
	{
		Answer++;  // 퀸을 모두 놓았으면 경우의 수 증가
		return;
	}

	for (int col = 0; col < N; col++)
	{
		board[row] = col;  // 현재 행에서 열의 위치 지정
		if (CanPlaceQueen(row))
			DFS(row + 1);  // 다음 행으로 이동하여 퀸 놓기 시도
	}
}

void PrintAnswer()
{
	cout << Answer << endl;  // 결과 출력
}

void Solve()
{
	Input();  // 입력 받기
	DFS(0);  // 퀸 놓기 시작
	PrintAnswer();  // 결과 출력
}

int main(void)
{
	Solve();

	return 0;
}


Chapter 08 슬라이딩윈도우 + 투포인터 ( 중요도 상 )

08-1 두 큐 합 같게 만들기 ( 2022 KAKAO TECH INTERNSHIP )

  • 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수를 return 하도록 solution 함수를 완성해주세요 단, 어떤 방법으로도 각 큐의 원소 합을 같게 만들 수 없는 경우, -1을 return 해주세요 (참고)
#include <string>
#include <vector>
#include <queue>
using namespace std;

int solution(vector<int> queue1, vector<int> queue2)
{
	// 각 큐의 합을 저장하는 변수
	long long sum1 = 0, sum2 = 0;
	// 큐 1과 큐 2를 저장하는 큐
	queue<int> q1, q2;

	// 큐 1에 있는 모든 수를 큐에 추가하고 각 수의 합을 구함
	for (int num : queue1)
	{
		q1.push(num);
		sum1 += num;
	}

	// 큐 2에 있는 모든 수를 큐에 추가하고 각 수의 합을 구함
	for (int num : queue2)
	{
		q2.push(num);
		sum2 += num;
	}

	// 큐 1과 큐 2에서 뽑아야 하는 수의 인덱스
	int idx1 = 0, idx2 = 0;
	// 더해진 총 수의 개수
	int total = queue1.size() + queue2.size();
	// 수행해야 하는 연산의 횟수
	int operations = 0;

	// 두 큐에서 뽑아야 할 수의 인덱스가 총 수의 개수보다 작을 때까지 반복
	while (idx1 < total && idx2 < total)
	{
		// 두 큐의 합이 같으면 연산을 수행하지 않고 연산 횟수를 반환
		if (sum1 == sum2)
		{
			return operations;
		}

		// 연산 횟수를 증가
		operations++;

		// 큐 1의 합이 큐 2의 합보다 작으면 큐 2에서 수를 뽑아 큐 1에 추가
		if (sum1 < sum2)
		{
			int cur = q2.front();
			q2.pop();

			q1.push(cur);
			sum1 += cur;
			sum2 -= cur;
			idx2++;
		}
		// 큐 2의 합이 큐 1의 합보다 작으면 큐 1에서 수를 뽑아 큐 2에 추가
		else
		{
			int cur = q1.front();
			q1.pop();

			q2.push(cur);
			sum2 += cur;
			sum1 -= cur;
			idx1++;
		}
	}

	// 큐 1과 큐 2의 합이 같아질 수 없으면 -1 반환
	return -1;
}

08-2 귀여운 라이언 ( 15565 )

  • 꿀귀 라이언 인형과, 마찬가지로 꿀귀인 어피치 인형이 N개 일렬로 놓여 있다. 라이언 인형은 1, 어피치 인형은 2로 표현하자 라이언 인형이 K개 이상 있는 가장 작은 연속된 인형들의 집합의 크기를 구하여라 (참고)
#include<bits/stdc++.h>
using namespace std;

int N, K;
vector<int> pos;

int main()
{
	int currentDoll;
	cin >> N >> K;

	for (int i = 0; i < N; ++i)
	{
		cin >> currentDoll;
		if (currentDoll == 1)
			pos.push_back(i);
	}

	if (pos.size() < K)
	{
		cout << -1 << endl;
		return 0;
	}

	int ans = 1e9;
	// size가 5 K가 3이라고 가정하고 상상해 보자
	for (int i = 0; i <= pos.size() - K; ++i)
	{
		ans = min(ans, pos[i + K - 1] - pos[i] + 1);
	}

	cout << ans << endl;
}

08-3 수 고르기 ( 2230 )

  • N개의 정수로 이루어진 수열 A[1], A[2], …, A[N]이 있다 이 수열에서 두 수를 골랐을 때(같은 수일 수도 있다), 그 차이가 M 이상이면서 제일 작은 경우를 구하는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

// MAX가 1e9면 안된다는것 주의
#define MAX 2000000000

int main()
{
	int answer = MAX;

	int n = 0, target = 0;
	cin >> n >> target;

	vector<int> vecNumber(n, 0);
	for (int i = 0; i < n; ++i)
		cin >> vecNumber[i];

	sort(vecNumber.begin(), vecNumber.end());

	// target이 0이라면 start가 OutOfBounds를 유발할 수 있다
	int start = 0, end = 0;
	while (start < n && end < n)
	{
		if (vecNumber[end] - vecNumber[start] < target)
			end = end + 1;
		else
		{
			answer = min(answer, vecNumber[end] - vecNumber[start]);
			start = start + 1;
		}
	}

	cout << answer << endl;
}

08-4 겹치는 건 싫어 (20922)

  • 이 수열에서 같은 정수를 K개 이하로 포함한 최장 연속 부분 수열의 길이를 구하는 프로그램을 작성해보자 (참고)
#include<bits/stdc++.h>
#define AMAX 200001
#define DMAX 100001
using namespace std;

int arr[AMAX];
int dup[DMAX];

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

	int n, k;
	cin >> n >> k;

	for (int i = 1; i <= n; i++)
	{
		cin >> arr[i];
	}

	int answer = 0;
	int left = 1;
	int right = 1;

	while (right <= n)
	{
		if (dup[arr[right]] < k)
		{
			dup[arr[right]]++;
			answer = max(answer, right - left + 1);
			right = right + 1;
		}
		else
		{
			dup[arr[left]]--;
			left = left + 1;
		}
	}
	cout << answer;

	return 0;
}

08-5 두 용액 ( 2470 )

  • 산성 용액과 알칼리성 용액의 특성값이 주어졌을 때, 이 중 두 개의 서로 다른 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액을 찾는 프로그램을 작성하시오 (참고)
#include<bits/stdc++.h>
using namespace std;

int main()
{
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n;
	cin >> n;

	vector<int> ans(2);
	int start = 0, end = n - 1, min = 2000000001;
	int arr[n];

	for (int i = 0; i < n; i++) cin >> arr[i];

	sort(arr, arr + n);
	while (start < end)
	{
		int sum = arr[start] + arr[end];

		if (abs(sum) < abs(min))
		{
			ans[0] = arr[start];
			ans[1] = arr[end];
			min = sum;
		}

		if (sum < 0) start++;
		else end--;
	}

	for (int i = 0; i < ans.size(); i++) cout << ans[i] << " ";

	return 0;
}

08-6 회전 초밥 ( 2531 )

  • 손님이 먹을 수 있는 초밥 가짓수의 최댓값을 구하는 프로그램을 작성하시오
#include<bits/stdc++.h>
using namespace std;

int N, d, k, c;
int dish, cnt, coupon, maxi = 0;
int sushi[300001];
int check[3001];
vector<int> eat;

int main()
{
	// N:접시수 d:초밥종류수 k:연속접시 C:쿠폰번호
	cin >> N >> d >> k >> c;

	// 회전벨트 위 초밥
	for (int i = 0; i < N; i++) cin >> sushi[i];

	// 슬라이딩 윈도우
	for (int i = 0; i < N; i++)
	{
		dish = 0; // 중복된 스시 체크
		coupon = 1; // 쿠폰 스시가 있는지 확인
		for (int j = i; j < i + k; j++)
		{
			if (check[sushi[j % N]] == 1) dish++; // 이미 먹은 초밥일 경우 중복 수 표시	
			else check[sushi[j % N]] = 1; // 처음 먹어보는 초밥일 경우 체크		

			if (sushi[j % N] == c) coupon = 0; // 쿠폰 스시 있을 시
		}

		maxi = max(maxi, k - dish + coupon); //연속으로 먹을 수 있는 최대접시 - 중복접시 + 쿠폰스시	
		memset(check, 0, sizeof(check)); // 체크 초기화
	}

	cout << maxi;
}

08-7 가장 긴 짝수 연속한 부분 수열 (large) ( 22862 )

  • 수열 S에서 최대 K번 원소를 삭제한 수열에서 짝수로 이루어져 있는 연속한 부분 수열 중 가장 긴 길이를 구해보자
#include <bits/stdc++.h>
using namespace std;

int arr[1000010];

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

	int n, k;
	cin >> n >> k;

	for (int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}

	int start = 0, end = 0;
	int cnt = (arr[start] & 1) ? 1 : 0;
	int max_len = 0;

	while (start < n && end < n)
	{
		while (end < n - 1)
		{
			if (arr[end + 1] & 1)
			{
				if (cnt < k)
					cnt++;
				else
					break;
			}
			end++;
		}

		max_len = max(max_len, end - start + 1 - cnt);

		if (arr[start] & 1)
			cnt--;

		start++;
	}

	cout << max_len;
}
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int result = 0;

    int numCount, maxDeleteCnt;
    cin >> numCount >> maxDeleteCnt;

    vector<int> numbers;
    int input;
    for (int i = 0; i < numCount; i++)
    {
        cin >> input;
        numbers.push_back(input);
    }

    int currentDeleteCnt = 0;
    int currentLen = 0;
    int left = 0;
    int right = 0;

    while (right < numCount)
    {
        if (numbers[right] % 2 == 0)
        {
            currentLen++;
            right++;
            result = max(result, currentLen - currentDeleteCnt);
        }
        else
        {
            if (currentDeleteCnt + 1 <= maxDeleteCnt)
            {
                currentDeleteCnt++;
                currentLen++;
                result = max(result, currentLen - currentDeleteCnt);
                right++;
            }
            else
            {
                if (right > left)
                {
                    if (numbers[left] % 2 == 1) currentDeleteCnt--;
                    left++;
                    currentLen--;
                }
                else
                {
                    left++;
                    right++;
                }
            }
        }
    }

    cout << result;

    return 0;
}

08-8 좋다 ( 1253 )

  • N개의 수가 주어지면 그 중에서 좋은 수의 개수는 몇 개인지 출력하라
#include<bits/stdc++.h>
using namespace std;

int N;
long long num[2010];
int Left, Right;
int ans;

int main(void)
{
	cin >> N;

	for (int i = 1; i <= N; i++)
	{
		cin >> num[i];
	}

	sort(num + 1, num + N + 1);

	for (int i = 1; i <= N; i++)
	{
		long long find = num[i];
		Left = 1;
		Right = N;

		// 투포인터 알고리즘 활용 
		while (Left < Right)
		{
			if (num[Left] + num[Right] == find)
			{
				if (Left == i) Left++;
				else if (Right == i) Right--;
				else
				{
					ans++;
					break;
				}
			}
			else if (num[Left] + num[Right] < find) Left++;
			else Right--;
		}
	}

	cout << ans;

	return 0;
}

08-9 부분합 ( 1806 )

  • 이 수열에서 연속된 수들의 부분합 중에 그 합이 S 이상이 되는 것 중, 가장 짧은 것의 길이를 구하는 프로그램을 작성하시오
#include<bits/stdc++.h>
using namespace std;

#define MAX 1e9

int arr[100000];
int main()
{
	int N, S;
	cin >> N >> S;
	for (int i = 0; i < N; i++)
	{
		cin >> arr[i];
	}

	int right = 0;
	int left = 0;
	int sum = arr[0];
	int minlength = MAX;
	while (left < N && right < N)
	{
		// 합이 크면 왼쪽 최소길이 갱신하고 움직이기
		if (S <= sum)
		{
			int length = right - left + 1;
			minlength = min(minlength, length);
			sum = sum - arr[left];
			left++;
		}
		// 합이 작으면 오른쪽 움직이기
		else
		{
			right++;
			sum = sum + arr[right];
		}
	}
    
	// 조건 만족할 수 없으면 0
	if (minlength == MAX)
	{
		minlength = 0;
	}
    
	cout << minlength;
	return 0;
}

08-10 용액 합성하기 ( 14921 )

  • 이 중 두 개의 용액을 혼합하여 만들 수 있는 0에 가장 가까운 특성값 B를 출력하시오
#include<bits/stdc++.h>
#define MAX 100000
using namespace std;

int a[MAX];
int answer = 200000000;

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

	int n;
	cin >> n;
	for (int i = 0; i < n; i++) cin >> a[i];

	int left = 0;
	int right = n - 1;
	sort(a, a + n);
	while (left < right)
	{
		// 두 용액 혼합
		int sum = a[left] + a[right];

		// 0에 더 가까우면 업데이트
		if (abs(sum) < abs(answer))
		{
			answer = sum;
		}

		if (sum == 0)
			break;
		else if (sum < 0)
			left++;
		else
			right--;
	}

	cout << answer;
	return 0;
}

08-11 고냥이 ( 16472 )

  • 문자열이 주어졌을 때 이 번역기가 인식할 수 있는 최대 문자열의 길이는 얼마인지가 궁금해졌다
#include<bits/stdc++.h>
using namespace std;
int alpha[26];

int main()
{
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

	int num, len, result = 0;
	string str;
	cin >> num >> str;
	len = str.size();
	int l = 0, r = 0, cnt = 0;

	while (l < len && r < len)
	{
		while (cnt <= num && r < len)
		{
			int idx = str[r] - 'a';
			alpha[idx]++;
			if (alpha[idx] == 1) cnt++;
			if (cnt <= num) result = max(result, r - l + 1);
			r = r + 1;
		}
		while (num < cnt && l < len)
		{
			int idx = str[l] - 'a';
			alpha[idx]--;
			if (alpha[idx] == 0) cnt--;
			l = l + 1;
		}
	}

	cout << result;
	return 0;
}


Chapter 09 문자열 ( 중요도 중 )

09-1 회문 ( 17609 )

  • 각 문자열이 회문인지, 유사 회문인지, 둘 모두 해당되지 않는지를 판단하여 회문이면 0, 유사 회문이면 1, 둘 모두 아니면 2를 순서대로 한 줄에 하나씩 출력한다

09-2 문자열 게임 2 ( 20437 )

  • T개의 줄 동안 문자열 게임의 3번과 4번에서 구한 연속 문자열의 길이를 공백을 사이에 두고 출력한다 만약 만족하는 연속 문자열이 없을 시 -1을 출력한다

09-3 회문은 회문아니야!! ( 15927 )

  • 팰린드롬이 아닌 가장 긴 부분문자열의 길이를 출력한다 그런 부분문자열이 없으면 -1을 출력한다

09-4 IPv6 ( 3107 )

  • 첫째 줄에, 입력으로 주어진 IPv6의 축약되지 않은 형태를 출력한다

09-5 잠수함식별 ( 2671 )

  • 입력에 들어있는 스트링을 읽고, 이것이 잠수함의 엔진소리를 나타내는 스트링인지 아니면 그냥 물속의 잡음인지를 판정한 후, 잠수함의 엔진 소리에 해당하는 스트링이면 “SUBMARINE”을 출력하고, 그렇지 않으면 “NOISE”를 출력한다

09-6 비슷한 단어 ( 2179 )

  • 첫째 줄에 S를, 둘째 줄에 T를 출력한다 단, 이 두 단어는 서로 달라야 한다 즉, 가장 비슷한 두 단어를 구할 때 같은 단어는 제외하는 것이다

09-7 문자열 폭팔 ( 9935 )

  • 첫째 줄에 모든 폭발이 끝난 후 남은 문자열을 출력한다

09-8 매칭 점수 ( 2019 KAKAO BLIND RECRUITMENT )

  • N줄에 걸쳐 문제에서 설명한 대로 문자열을 정렬한 결과를 출력한다
// https://school.programmers.co.kr/learn/courses/30/lessons/42893

09-9 가사 검색 ( 2020 KAKAO BLIND RECRUITMENT )

  • 가사에 사용된 모든 단어들이 담긴 배열 words와 찾고자 하는 키워드가 담긴 배열 queries가 주어질 때, 각 키워드 별로 매치된 단어가 몇 개인지 순서대로 배열에 담아 반환하도록 solution 함수를 완성해 주세요
// https://school.programmers.co.kr/learn/courses/30/lessons/60060

09-10 문자열 압축 ( 2020 KAKAO BLIND RECRUITMENT )

  • 압축할 문자열 s가 매개변수로 주어질 때, 위에 설명한 방법으로 1개 이상 단위로 문자열을 잘라 압축하여 표현한 문자열 중 가장 짧은 것의 길이를 return 하도록 solution 함수를 완성해주세요
// https://school.programmers.co.kr/learn/courses/30/lessons/60057


Chapter 10 DP ( 중요도 하 )

10-1 타일 채우기 ( 2133 )

  • 3×N 크기의 벽을 2×1, 1×2 크기의 타일로 채우는 경우의 수를 구해보자
// https://www.youtube.com/watch?v=E_-dFdvy288 참고
// https://velog.io/@matcha_/%EB%B0%B1%EC%A4%80-2133-%ED%83%80%EC%9D%BC-%EC%B1%84%EC%9A%B0%EA%B8%B0-C-DP 참고

#include <iostream>
using namespace std;

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

    int N;
    cin >> N;

    int d[31];
    d[0] = 1;  // 초기 조건: 세로로 놓인 2x0 타일 1개
    d[1] = 0;  // 초기 조건: 세로로 놓인 2x1 타일은 만들 수 없음
    d[2] = 3;  // 초기 조건: 가로로 놓인 2x1 타일 3개

    for (int n = 3; n <= N; n++)
    {
        if (n % 2 != 0)
            d[n] = 0;  // 홀수인 경우 타일을 만들 수 없음
        else
        {
            for (int i = 2; i <= N; i = i + 2)
            {
                if (i == 2)
                    d[n] = d[n - i] * d[2];  // 2x2 타일 1개로 구성
                else if (0 <= (n - i))
                    d[n] = d[n] + d[n - i] * 2;  // 2x(i) 타일을 추가로 넣을 때의 경우의 수
            }
        }
    }
    cout << d[N];
}

10-2 가장 큰 정사각형 ( 1915 )

  • 이 배열에서 1로 된 가장 큰 정사각형의 크기를 구하는 프로그램을 작성하시오
// https://www.acmicpc.net/problem/1915

10-3 미로에 갇힌 상근 ( 5069 )

  • 상근이가 있는 방에서 시작해서 방을 n번 이동해서, 다시 원래 있던 방으로 돌아오는 경로의 수를 구하려고 한다
// https://www.acmicpc.net/problem/5069

10-4 방법을 출력하지 않는 숫자 맞추기 ( 13392 )

  • 상근이가 있는 방에서 시작해서 방을 n번 이동해서, 다시 원래 있던 방으로 돌아오는 경로의 수를 구하려고 한다
// https://www.acmicpc.net/problem/13392

10-5 영훈이의 색칠공부 ( 14578 )

  • N과, 장난감의 수가 주어질 때, 트리를 장식하는 경우의 수를 출력하는 프로그램을 작성하시오
// https://www.acmicpc.net/problem/14578

10-6 경로 찾기 ( 1513 )

  • 오락실을 0개 방문했을 때부터, C개 방문했을 때 까지 경우의 수를 출력하는 프로그램을 작성하시오
// https://www.acmicpc.net/problem/1513

10-7 로봇 조종하기 ( 2169 )

  • 탐사한 지역들의 가치의 합이 최대가 되도록 하는 프로그램을 작성하시오
// https://www.acmicpc.net/problem/2169

10-8 욕심쟁이 판다 ( 1937 )

  • 이 판다가 최대한 많은 칸을 이동하려면 어떤 경로를 통하여 움직여야 하는지 구하여라
// https://www.acmicpc.net/problem/1937

10-9 타일 채우기 ( 2718 )

  • 타일을 채우는 방법의 개수를 출력하는 프로그램을 작성하시오
// https://www.acmicpc.net/problem/2718

10-10 파이프 옮기기 2 ( 17069 )

  • 파이프의 한쪽 끝을 (N, N)로 이동시키는 방법의 개수를 구해보자
// https://www.acmicpc.net/problem/17069

// https://nurbscalculator.in/
class BSpline
{
private:
    std::vector<double> knots;
    std::vector<std::vector<double>> controlPoints;
    int degree;

public:
    BSpline(const std::vector<double>& knotVector, const std::vector<std::vector<double>>& controlPts, int deg)
        : knots(knotVector), controlPoints(controlPts), degree(deg) {}

    double basisFunction(int i, int k, double t) {
        if (k == 1) {
            if (knots[i] <= t && t < knots[i + 1]) return 1;
            else return 0;
        }

        double term1 = 0, term2 = 0;

        if (knots[i + k - 1] - knots[i] != 0)
            term1 = (t - knots[i]) / (knots[i + k - 1] - knots[i]) * basisFunction(i, k - 1, t);

        if (knots[i + k] - knots[i + 1] != 0)
            term2 = (knots[i + k] - t) / (knots[i + k] - knots[i + 1]) * basisFunction(i + 1, k - 1, t);

        return term1 + term2;
    }

    std::vector<double> evaluate(double t) {
        std::vector<double> point(controlPoints[0].size(), 0.0);

        for (int i = 0; i < controlPoints.size(); ++i) {
            double basis = basisFunction(i, degree + 1, t);
            for (int j = 0; j < point.size(); ++j) {
                point[j] += basis * controlPoints[i][j];
            }
        }

        return point;
    }
};

int main()
{
    // 제어점의 수를 n이라고 하면 B-spline 곡선의 차수를 k라고 합니다
    // 그러면 knots 벡터의 크기는 n + k + 1이어야 합니다 ( Clamped at start, Clamped at end )
    std::vector<double> knots = { 0, 0, 0, 0, 1, 1, 1, 1 };
    std::vector<std::vector<double>> controlPoints = { {0, 0, 1}, {1, 1, 0}, {2, -1, 0 }, {3, 0, 0} };
    int degree = 3;

    BSpline spline(knots, controlPoints, degree);

    // Evaluate the spline at t = 0.4
    std::vector<double> result = spline.evaluate(0.4);

    // Output the result
    std::cout << "Result at t = 0.4: (" << result[0] << ", " << result[1] << ", " << result[2] << ")" << std::endl;

    return 0;
}


맨 위로 이동하기

댓글남기기