개요

가장 긴 바이토닉 부분 수열을 찾는 문제

바이토닉 부분 수열

아이디어

알고리즘 : Dynamic Programming (DP)

 

풀이 후 다른 블로그를 참고해보니 `LIS(증가하는 부분수열의 크기) + LDS(감소하는 부분수열의 크기)`를 구하여서

"합산한 값의 최댓값에 -1" 방법으로 구하는 것을 확인했습니다.

(* -1을 하는 이유는 LIS와 LDS를 합치면 같은 원소가 중복해서 합산되기 때문.)

 

풀이 당시 비슷하지만 다른 접근 방식으로 문제를 풀어서 기록을 남기게 되었습니다.

arr[N] = {1, 5, 2, 1, 4, 3, 4, 5, 2, 1} 기준으로 풀이하겠습니다.

 

dp[2][N]을 선언했습니다.

dp[0][i]에는 해당 i번째에 오름차순 기준으로 최대 길이가 저장되게 됩니다.

dp[1][i]에는 해당 i번째에 내림차순 기준으로 최대 길이가 저장되게 됩니다.

 

1. dp[2][N]을 1로 초기화 합니다.

해당 위치는 모두 "오름차순의 시작지점" 또는 "내림차순의 시작지점"이 될 수 있기 때문입니다.

1 5 2 1 4 3 4 5 2 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1

 

2. i=1번째 인덱스부터 순회하며, j=i-1부터 0번째까지 역순으로 탐색하며 최대길이를 업데이트 합니다.

i = 1 -> arr[1] = 5
j = 0 -> arr[0] = 1
1 5 2 1 4 3 4 5 2 1
1 2 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1

arr[i]=5 > arr[j]=1 이라면 오름차순이 이어지게 된다.

if(arr[i] > arr[j]){
    // 오름차순 인지, 오름차순 시작 지점(or 최대)인지
    dp[0][i] = Math.max(dp[0][j] + 1, dp[0][i]);
}

 

업데이트 과정은 다음과 같다.

dp[0][0] + 1 = 1 + 1 = 2

dp[0][1] = 1

=> dp[0][1] = max(2, 1)

 


i = 2 -> arr[2] = 2
j = 1 -> arr[1] = 5
1 5 2 1 4 3 4 5 2 1
1 2 1 1 1 1 1 1 1 1
1 1 3 1 1 1 1 1 1 1

arr[i]=2 < arr[j]=5 라면 내림차순으로 변경되는 지점이다.

내림차순이 이어지거나 시작되는 지점이기도 하다.

if(arr[i] < arr[j]){
    // 최대 인지, 내림차순 이어지는 지점 인지, 내림차순으로 변경된지점인지
    dp[1][i] = Math.max(dp[1][i], Math.max(dp[1][j] + 1, dp[0][j] + 1));
}

업데이트 과정은 다음과 같다.

내림차순으로 변경되는 지점 : dp[0][1] + 1 = 2 + 1 = 3

내림차순이 이어지거나 시작되는 지점 : dp[1][1] + 1 = 1 + 1 = 2

dp[1][2] = 1

=> dp[1][2] = max(1, max(2, 3))

 

i = 2 -> arr[2] = 2
j = 0 -> arr[0] = 1
1 5 2 1 4 3 4 5 2 1
1 2 2 1 1 1 1 1 1 1
1 1 3 1 1 1 1 1 1 1

arr[i]=2 > arr[j]=1 이라면 오름차순이 이어지게 된다. ( 맨 처음에 수행한 과정과 동일하다.)

if(arr[i] > arr[j]){
    // 오름차순 인지, 오름차순 시작 지점(or 최대)인지
    dp[0][i] = Math.max(dp[0][j] + 1, dp[0][i]);
}

업데이트 과정은 다음과 같다.

dp[0][0] + 1 = 1 + 1 = 2

dp[0][2] = 1

=> dp[0][2] = max(2, 1)


3. 2번의 과정을 반복하면 됩니다.

i = 3 -> arr[3] = 1
j = 2 -> arr[2] = 2
1 5 2 1 4 3 4 5 2 1
1 2 2 1 1 1 1 1 1 1
1 1 3 4 1 1 1 1 1 1
i = 4 -> arr[4] = 4
1 5 2 1 4 3 4 5 2 1
1 2 2 1 3 1 1 1 1 1
1 1 3 4 3 1 1 1 1 1
i = 5 -> arr[5] = 3
1 5 2 1 4 3 4 5 2 1
1 2 2 1 3 3 1 1 1 1
1 1 3 4 3 4 1 1 1 1
i = 6 -> arr[6] = 4
1 5 2 1 4 3 4 5 2 1
1 2 2 1 3 3 4 1 1 1
1 1 3 4 3 4 3 1 1 1
i = 7 -> arr[7] = 5
1 5 2 1 4 3 4 5 2 1
1 2 2 1 3 3 4 5 1 1
1 1 3 4 3 4 3 1 1 1
i = 8 -> arr[8] = 2
1 5 2 1 4 3 4 5 2 1
1 2 2 1 3 3 4 5 2 1
1 1 3 4 3 4 3 1 6 1
i = 9 -> arr[9] = 1
1 5 2 1 4 3 4 5 2 1
1 2 2 1 3 3 4 5 2 1
1 1 3 4 3 4 3 1 6 7

4.dp[2][N]에서 최대값을 찾는다.

dp[2][N] 배열에서 최댓값은 7이다.

코드

import java.io.*;
import java.util.*;

public class Main {
    static int[] arr;
    static int[][] dp;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int N = Integer.parseInt(br.readLine());
        arr = new int[N];
        dp = new int[2][N]; // row 0 : 오름차순, row 1 : 내림차순

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
            dp[0][i] = 1;
            dp[1][i] = 1;
        }

        for(int i=1; i<N; i++){
            for(int j=i-1; j>=0; j--){
                if(arr[i] > arr[j]){
                    // 오름차순 인지, 오름차순 시작 지점(or 최대)인지
                    dp[0][i] = Math.max(dp[0][j] + 1, dp[0][i]);
                }
                else if(arr[i] < arr[j]){
                    // 그대로 인지, 내림차순 이어지는 지점 인지, 내림차순으로 변경된지점인지
                    dp[1][i] = Math.max(dp[1][i], Math.max(dp[1][j] + 1, dp[0][j] + 1));
                }
            }
        }

        int ans = 0;
        for(int i=0; i<2; i++){
            for(int j=0; j<N; j++){
                ans = Math.max(ans, dp[i][j]);
            }
        }

        bw.write(String.valueOf(ans));

        bw.flush();
        bw.close();
        br.close();
    }

}

문제출처

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

 

반응형

최대공약수 구하기

int gcd(int a, int b)
{
	int c;
	while (b != 0) { // 나눌 수가 0이 될때까지 반복
		c = a % b;
		a = b;
		b = c;
	}
	return a;
}
반응형

[아이디어]

알고리즘 : BFS

 

베이스 캠프를 선정할 때,

편의점부터 시작해 BFS로 모든 베이스 캠프의 위치를 찾아내고,

vector에 담아 문제의 우선순위 조건에 맞춰 정렬해주었습니다.

(우선순위 조건 : 최단거리, min row, min col)

 

사람 -> 편의점 최단 거리 구할 때,

BFS(시작 : 편의점 위치, 도착 : 사람 위치)로 했습니다.

이렇게하면 현재 사람위치에서 동서남북으로 확인하면

다음 위치로 이동할 때, 최단거리가 얼마인지를 알 수 있기 때문입니다.

 

사람이 편의점이나 베이스캠프에 도착하면,

다른 사람은 지날 수 없는 벽이 생깁니다.

저는 이 부분을

이 사람이 격자내 있는지, 편의점에 도착을 했는지 flag를 두어서

flag의 상태를 보고

격자 밖 or 격자 안,

편의점 도착 전 or 도착 후

를 판단했습니다.

 

예를 들면,

isInside == false && isGoal == false

이 사람은 아직 격자에 들어온적이 없습니다.

 

isInside == true && isGoal == false

이 사람은 아직 격자에 들어왔지만, 편의점에 도착하지 못 했습니다.

 

isInside == true && isGoal == true

이 사람은 아직 격자에 들어왔고, 편의점에 도착했지만, 아직 격자 밖으로 안 나갔습니다.

 

isInside == false && isGoal == true

이 사람은 아직 격자에 들어왔고, 편의점도 도착했습니다. 그렇기에 격자 밖으로 내보냅니다.

 

[요약]

t분일 때 t(m)번째 사람이 입장합니다.

t <= m이면 m번째 사람이 입장합니다.

 

격자 크기는 n*n이며, 좌상단은 (1,1)로 시작됩니다.

 

 

아래 3가지 행동이 1분내 이루어집니다.

 

1. 모든 사람 이동

격자내, 모든 사람이 가고 싶은 편의점 방향으로 이동합니다.

이동곳을 선택하는 방법은 "최단거리"이며,

동일한 거리라면, "행"이 작고,

동일한 행위치라면, "열"이 작아야합니다.

우선순위 : ( 상, 좌, 우, 하 )

 

2. 편의점 도착

if ( 사람이 편의점에 도착하면 ) then (편의점은 다른 사람이 지날 수 없습니다.)

"단, 모든 사람이 이동후에 다른 사람이 지날 수 없게 됩니다"를 유의해주세요.

 

3. 사람 입장

현재 시간이 t분이고 t <= m이면, 사람이 입장합니다.

입장할 곳은 가고 싶은 편의점과 '가장 가까운 베이스캠프'입니다.

편의점과 가장 가까워야하며,

거리가 같으면 행이 작은 곳,

행이 같으면 열이 작은 곳

을 선택하면 됩니다.

또한 입장하는 동안 시간은 흐르지 않습니다.

그리고, 사람이 베이스캠프에 들어가면

해당 베이스캠프를 들어온 사람을 포함해, 다른 사람이 지날 수 없습니다.

 

[코드]

#include <iostream>
#include <queue>
#include <algorithm>
#include <climits>
using namespace std;

#define FOR(i,s,e) for(int i=(s); i<(e); ++i)
#define N_MAX (15+1)
#define M_MAX (30+1)

int n, m;
struct NODE
{
    int y, x;
    bool isInside; // 사람이 내부에 있는지 (있으면 true, 없으면 false)
    bool isGoal; // 사람이 편의점에 도착했는지 (했으면 true, 안 했으면 false)
};
int basecamp[N_MAX][N_MAX]; // 베이스캠프 위치
NODE market_pos[M_MAX]; // 편의점 위치 리스트
NODE basecamp_pos[(N_MAX * N_MAX) - M_MAX]; // 베이스캠프 위치 리스트
int wall[N_MAX][N_MAX]; // 벽
NODE person[M_MAX]; // 사람
int t;
int dy[4] = { -1,0,0,1 };
int dx[4] = { 0,-1,1,0 };


void input() {
    cin >> n >> m;
    int idx = 0;
    FOR(y, 1, (n + 1)) {
        FOR(x, 1, (n + 1)) {
            cin >> basecamp[y][x];

            if (basecamp[y][x] == 1) {
                basecamp_pos[idx].y = y;
                basecamp_pos[idx++].x = x;
            }
        }
    }

    FOR(i, 1, (m + 1)) {
        // 편의점 초기화
        NODE* now = &market_pos[i];
        cin >> now->y >> now->x;

        // 사람 초기화
        person[i].y = -1;
        person[i].x = -1;
        person[i].isInside = false;
        person[i].isGoal = false;
    }
}

bool isPersonInside() {
	/*
    격자 안에 있는 사람이 있는지 확인합니다.
    사람이 있으면 true
    사람이 없으면 false
    */
    FOR(i, 1, (m + 1)) {
        if (person[i].isInside == true) return true;
    }
    return false;
}

void select_next_pos(int pNum) {
    /*
    편의점 -> 사람까지 몇칸 필요한지 계산
    다음 이동할 위치 선정
    */
    queue<pair<int, int>> q;
    int dist[N_MAX][N_MAX] = { 0, }; // 편의점 -> 사람까지 거리를 기록
    NODE start = market_pos[pNum];

    q.push(make_pair(start.y, start.x));
    dist[start.y][start.x] = 1;

    while (!q.empty()) {
        pair<int, int> now = q.front();
        q.pop();

        FOR(i, 0, 4) {
            int ny = now.first + dy[i];
            int nx = now.second + dx[i];

            if (ny<1 || ny>n || nx<1 || nx>n) continue; // 격자 밖
            if (dist[ny][nx] != 0) continue; // 이미 지나온 곳
            if (wall[ny][nx] == 1) continue; // 지나갈 수 없는곳

            dist[ny][nx] = dist[now.first][now.second] + 1;
            q.push(make_pair(ny, nx));
        }
    }


    // 상 좌 우 하 우선순위로 선택
    NODE* now_person = &person[pNum];
    int my = INT_MAX;
    int mx = INT_MAX;
    int mdist = INT_MAX;

    // 최단 거리
    FOR(i, 0, 4) {
        int ny = now_person->y + dy[i];
        int nx = now_person->x + dx[i];

        if (ny<1 || ny>n || nx<1 || nx>n) continue; // 격자 밖
        if (dist[ny][nx] == 0) continue; // 기록에 없는 곳

        mdist = min(mdist, dist[ny][nx]);
    }

    // 다음 위치
    FOR(i, 0, 4) {
        int ny = now_person->y + dy[i];
        int nx = now_person->x + dx[i];

        if (ny<1 || ny>n || nx<1 || nx>n) continue; // 격자 밖
        if (mdist != dist[ny][nx]) continue; // 최단거리랑 다르면

        if (my > ny) {
            my = ny;
            mx = nx;
        }
        if (my == ny) {
            if (mx > nx) {
                my = ny;
                mx = nx;
            }
        }
    }

    // 사람 이동
    now_person->y = my;
    now_person->x = mx;
}

void all_person_move() {
    /*
    모든 사람 이동
    */

    FOR(pNum, 1, (m + 1)) {
        if (!person[pNum].isInside) continue; //격자내 있는 사람아니면 pass

        select_next_pos(pNum);
    }
}

void check_person_arrive_market() {
    /*
    편의점에 도착한 사람 있는지
    */

    FOR(pNum, 1, (m + 1)) {
        NODE* now_market = &market_pos[pNum];
        NODE* now_person = &person[pNum];

        if (now_market->y == now_person->y && now_market->x == now_person->x) {
            now_person->isGoal = true;
        }
    }

    FOR(pNum, 1, (m + 1)) {
        NODE* now_person = &person[pNum];

        if (now_person->isGoal == true) { // 도착했다면
            now_person->isInside = false; // 내보내고

            wall[now_person->y][now_person->x] = 1; // 벽세움
        }
    }
}

bool find_basecamp(pair<pair<int, int>, int> left, pair<pair<int, int>, int> right) {
    if (left.second < right.second) return true;
    if (left.second > right.second) return false;

    if (left.first.first < right.first.first) return true;
    if (left.first.first > right.first.first) return false;
    
    if (left.first.second < right.first.second) return true;
    if (left.first.second > right.first.second) return false;

    return false;
}

pair<int, int> select_basecamp(int pNum) {
    /* 
    bfs 탐색으로 
    편의점 -> 베이스캠프 찾고
    찾은 베이스캠프 중 정렬해서 가장 가깝고 row작고 col작은 베이스캠프 리턴
    */

    queue<pair<int, int>> q;
    int dist[N_MAX][N_MAX] = { 0, };
    NODE start = market_pos[pNum];
    vector<pair<pair<int, int>, int>> v;

    q.push(make_pair(start.y, start.x));
    dist[start.y][start.x] = 1;

    while (!q.empty()) {
        pair<int, int> now = q.front();
        q.pop();

        FOR(i, 0, 4) {
            int ny = now.first + dy[i];
            int nx = now.second + dx[i];

            if (ny<1 || ny>n || nx<1 || nx>n) continue; // 격자 밖
            if (dist[ny][nx] != 0) continue; // 이미 지나온 곳
            if (wall[ny][nx] == 1) continue; // 지나갈 수 없는곳

            dist[ny][nx] = dist[now.first][now.second] + 1;
            if (basecamp[ny][nx] == 1) {
                v.push_back(make_pair(make_pair(ny, nx), dist[ny][nx]));
            }
            q.push(make_pair(ny, nx));
        }
    }

    sort(v.begin(), v.end(), find_basecamp);

    return make_pair(v[0].first.first, v[0].first.second);
}

void person_enter_basecamp(int pNum) {
    /*
    t번째 사람 베이스캠프로 들어가기
    */
    pair<int,int> p = select_basecamp(pNum); // 선택한 베이스캠프
    person[pNum].y = p.first;
    person[pNum].x = p.second;
    person[pNum].isInside = true; // 들어갔다.

    wall[p.first][p.second] = 1; // 벽 세워짐
}

bool all_pass() {
	/*
    	모두 편의점에 도착했는지
    */
    FOR(i, 1, (m + 1)) {
        NODE* now = &person[i];

        if (now->isGoal == true && now->isInside == false) {

        }
        else {
            return false;
        }
    }
    return true;
}

void sol() {
    t = 1;
    while (1) {
        if (isPersonInside() == true) {
            all_person_move();
            check_person_arrive_market();
        }
        if (t <= m) {
            person_enter_basecamp(t);
        }

        if (all_pass() == true) break;
        ++t; // 1분 증가
    }
    cout << t;
}

int main() {
    // 여기에 코드를 작성해주세요.
    input();
    sol();

    return 0;
}

 

[문제 출처]

https://www.codetree.ai/training-field/frequent-problems/problems/codetree-mon-bread/description?page=1&pageSize=20

반응형

[아이디어]

해시맵을 사용해 구현했습니다.

 

L과 t의 정수 범위가 상당히 크기에 시간초과를 고려해야합니다.

최대 Q의 경우 100,000이고,

사람의 경우 15,000이므로

해당 변수를 저장하고 관계를 파악하는 식으로

시간초과를 관리했습니다.

 

먼저 원형의 벨트를 풀어 1차원 배열의 형태로 생각하였습니다.

또한 이 배열은 양 끝이 연결되어

L = 5일때, 마지막 index인 4에서 한칸 시계방향(오른쪽)으로 이동한다면

index가 0인 위치로 이동합니다.

[요약]

원형의 벨트는 시계방향으로 t가 1증가할 때 1칸 회전함

 

i) 주방장의 초밥 만들기

시각 t에 회전 벨트가 시계 방향으로 회전한 직후,

주방장은 시각 t위치가 x인 의자 앞에 있는 벨트위에 name이름을 부착한 초밥을 올려둠

(단, 같은 위치에 여러 초밥을 올려두어도 OK,

같은 이름의 초밥도 OK)

 

ii) 손님 입장

시각 t에 회전 벨트가 시계 방향으로 회전한 직후,

이름이 name인 사람이 위치가 x인 의자에 앉는다.

(해당 위치엔 사람이 없다고 가정한다.)

이때 부터, 위치 x 앞으로 오는 초밥들중

자신의 이름이 적혀있는 초밥을 정확히 n개를 먹고 자리를 떠난다.

만약 시각 t위치 x에 자신의 이름이 적힌 초밥이 있다면 앉자마자 먹는다.

(여러개가 있다면 여러개 먹는다)

(단, 오는 손님은 모두 다르다 = 재방문이 없다)

 

Ii) 촬영

시각 t에 회전 벨트가 시계 방향으로 회전한 직후,

이름이 name인 사람이 위치 x 앞으로 오는 초밥들을 먹고,

상태를 촬영해

남은 사람 수, 초밥 수를 출력한다.

 

[방법]

초밥을 생성하는 명령(100)만 탐색해

해당 초밥의 소멸시간을 계산함.

 

모든 사건이 일어난 명령들의 집합에

초밥의 소멸시간을 계산한 집합을

합쳐서

t가 작은 시각부터 사건의 순서에 맞게 집합을 정렬함

그러면 아래와 같은 형태가 완성됨.

 

합쳐진 집합을 탐색하며

초밥이 생성되면 sushi_cnt += 1

초밥이 사라지면 sushi_cnt -= 1

손님이 방문하면 customer +=1

초밥이 사라지면 누가 먹었는지 확인해 그 사람이 앞으로 먹어야하는 초밥 갯수 -1한 후

앞으로 먹어야하는 초밥 갯수가 0가 되어 손님이 나가면 customer -= 1

 

[회고]

시간 초과 해결하기위해

for(사람 이름 : 사람 목록) {
	for(cmd == 100 : 명령 집합) {
    
    }
}

 

위와 같은 2중 for문을 구현한다면

사람의 최대 수 15,000명

최대 명령수 100,000로

15,000 x 100,000 = 15억 이므로

시간초과 나기에

분리해주는 작업을 거쳤다.

[코드]

#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>

using namespace std;

#define FOR(i,s,e) for(int i=s; i<e; ++i)
#define Q_MAX 10

int L, Q;
struct CMD {
    int cmd, x, t, n;
    string name;
};
vector<CMD> commands;
unordered_map<string, int> sitting_position; // 손님이 앉은 자리 매핑
unordered_set<string> visitting_customer_names; // 방문한 손님 목록 저장 ( 재방문은 없으므로 중복이 없다 )
unordered_map<string, int> visit_customer_time; // 손님이 방문한 시간대
unordered_map<string, int> eat_cnt; // 손님이 먹어야할 초밥 갯수 매핑
vector<CMD> v;
int sushi_cnt; // 남은 초밥 갯수
int customer_cnt; // 남은 손님 명수

void input() {
    cin >> L >> Q;
    FOR(i, 0, Q) {
        int cmd = -1, t = -1, x = -1, n = -1;
        string name = "";

        cin >> cmd;

        if (cmd == 100) {
            cin >> t >> x >> name;
        }
        else if (cmd == 200) {
            cin >> t >> x >> name >> n;

            visitting_customer_names.insert(name);
            sitting_position[name] = x; // 오는 손님은 모두 다르다 ( 재방문 x )
            visit_customer_time[name] = t; // 손님이 방문한 시각
            eat_cnt[name] = n;
        }
        else if (cmd == 300) cin >> t;

        CMD tmp;
        tmp.cmd = cmd;
        tmp.t = t;
        tmp.x = x;
        tmp.name = name;
        tmp.n = n;
        commands.push_back(tmp);
    }
}

bool mcmp(CMD left, CMD right) {
    if (left.t > right.t) return false;
    if (left.t < right.t) return true;

    if (left.cmd > right.cmd) return false;
    if (left.cmd < right.cmd) return true;

    return false;
}

void sol() {
    FOR(i,0, commands.size()) {// 생성된 초밥이 언제 없어질지 기록
        if (commands[i].cmd != 100) continue; // 초밥이 생성된 시점이 아니라면 pass
            
        CMD sushis = commands[i];
        // 해당 손님(commads[i].name)이 먹어야할 초밥
        /*
            시간 초과 관리를 위해 초밥생성 관련 명령 분리

            최대 명령 수 : 100,000
            회대 사람 수 : 15,000
        */

        int meet_sushi_cutomer_time = 0; // 초밥과 손님이 만나게 할 수 있는 보정 시간
        int eat_sushi_time = 0; //초밥을 먹는 시간

        if (sushis.t < visit_customer_time[sushis.name]) { 
            /*
                손님 입장전에 손님의 초밥이 벨트위에 있음
            */

            // 현재 초밥의 위치
            int now_sushi_position = sushis.x;

            // 손님이 방문한 시점과 먹어야할 초밥이 들어온 시점의 차이
            int diff_t = visit_customer_time[sushis.name] - sushis.t;


            // 손님이 입장했을때, 회전 초밥 위치 이동
            // 
            // 손님이 입장하기 전에 먹어야할 초밥이 들어와있으므로
            // 손님이 들어올 시점에 초밥이 어디로 이동할지 구해줌
            now_sushi_position = ((now_sushi_position + diff_t) % L);

            if (sitting_position[sushis.name] >= now_sushi_position) {
                /*
                    손님이 초밥보다 시계방향으로 더 먼쪽에 앉아 있음
                */

                // 손님이 초밥을 먹기위해 걸리는 시간 = 손님이 앉은 자리 위치 - 현재 초밥 위치
                meet_sushi_cutomer_time = sitting_position[sushis.name] - now_sushi_position;
            }
            else if (sitting_position[sushis.name] < now_sushi_position) {
                /*
                    손님이 초밥보다 반시계방향으로 더 먼쪽에 앉아 있음
                */

                // 손님이 초밥을 먹기위해 걸리는 시간 = (손님이 앉은 자리 위치 + 의자의 갯수(1싸이클)) - 현재 초밥 위치
                meet_sushi_cutomer_time = (sitting_position[sushis.name] + L) - now_sushi_position;
            }

            // 손님을 초밥을 먹을 수 있는 시간 = 손님이 방문한 시간대 + 손님이 초밥을 먹기위해 걸리는 시간
            eat_sushi_time = visit_customer_time[sushis.name] + meet_sushi_cutomer_time;
        }
        else { 
            /*
                손님 입장 후 초밥이 벨트위로 올라옴
            */

            // 현재 초밥의 위치
            int now_sushi_position = sushis.x;

            if (sitting_position[sushis.name] >= now_sushi_position) {
                /*
                    손님이 초밥보다 시계방향으로 더 먼쪽에 앉아 있음
                */

                // 손님이 초밥을 먹기위해 걸리는 시간 = 손님이 앉은 자리 위치 - 현재 초밥 위치
                meet_sushi_cutomer_time = sitting_position[sushis.name] - now_sushi_position;
            }
            else if (sitting_position[sushis.name] < now_sushi_position) {
                /*
                    손님이 초밥보다 반시계방향으로 더 먼쪽에 앉아 있음
                */

                // 손님이 초밥을 먹기위해 걸리는 시간 = (손님이 앉은 자리 위치 + 의자의 갯수(1싸이클)) - 현재 초밥 위치
                meet_sushi_cutomer_time = (sitting_position[sushis.name] + L) - now_sushi_position;
            }

            // 손님을 초밥을 먹을 수 있는 시간 = 초밥이 들어온 시간 + 손님이 초밥을 먹기위해 걸리는 시간
            eat_sushi_time = sushis.t + meet_sushi_cutomer_time;
        }

        // 초밥이 사라지는 시점을 저장
        CMD tmp;
        tmp.cmd = sushis.cmd + 1;
        tmp.t = eat_sushi_time;
        tmp.x = -1;
        tmp.name = sushis.name;
        tmp.n = -1;
        v.push_back(tmp);
    }

    // 전체 명령과 초밥이 사라지는 시점에 대한 정보를 합침
    commands.insert(commands.end(), v.begin(), v.end());

    // t가 작을 수록, 명령이 작을수록 우선순위를 부여해 정렬함
    // 명령 우선순위 (100 -> 101 -> 200 -> 300)
    sort(commands.begin(), commands.end(), mcmp);

    FOR(i, 0, commands.size()) {
        /*
            전체 명령과 초밥이 사라지는 시점에 대한 정보를 합친것을 바탕으로
            cmd에 따른 작업을 수행
        */

        if (commands[i].cmd == 100) {
            /*
                초밥이 생성됨
            */
            ++sushi_cnt;
        }
        else if (commands[i].cmd == 101) {
            /*
                초밥이 사라짐
            */
            --sushi_cnt;

            // 초밥이 사라졌으므로 손님을 먹을 초밥 갯수를 줄임
            string now_name = commands[i].name;
            eat_cnt[now_name] = eat_cnt[now_name] - 1;
            if (eat_cnt[now_name] <= 0) --customer_cnt; // 손님이 먹을 초밥의 갯수가 0이하가 되었다면 방문한 손님 수는 줄어듬
        }
        else if (commands[i].cmd == 200) {
            /*
                손님이 방문함
            */
            ++customer_cnt;
        }
        else if (commands[i].cmd == 300) {
            /*
                촬영
            */
            cout << customer_cnt << " " << sushi_cnt << '\n';
        }
    }
}

int main() {
    // 여기에 코드를 작성해주세요.
    freopen("input.txt", "r", stdin);
    input();
    sol();

    return 0;
}

 

[문제 출처]

https://www.codetree.ai/training-field/frequent-problems/problems/codetree-omakase/description?page=1&pageSize=20

반응형

[아이디어]

단순 구현으로 해결했습니다.

 

[요약]

i) 기사 이동 방법

왕의 기사와 방향을 명령을 내림

해당 기사는 상하좌우로 이동

if ( 이동할 위치에 다른 기사가 있다면 ) 해당 기사도 연쇄적으로 밀림

if ( 벽이 있다면 ) 이동 불가

if ( 명령 내릴 기사가 없다면 ) 어떤 작업도 시작되지 않음

 

ii) 대결 대미지

명령 받은 기사다른 기사 밀면 "밀려난"기사는 피해를 입는다.

명령 받은기사는 어떤 피해도 없다.

기사"모두 밀린" 뒤 대미지를 입는다.

각 기사가 "이동한 위치"에서 (r,c) 좌상단을 기준으로 h x w의 영역 내에 함정 수만큼 피해를 입음

if ( 현재체력 < 피해 입을 체력 ) 기사는 체스판에서 사라짐

 

 

[방법]

for ( 왕의 명령 수만큼 반복 )
    if ( 기사가 체스판위에 아직 있는지 확인 ) 없다면 pass
    
    기사 위치를 백업 (knigh_map) // (tmp_knight_map 을 활용해 기사 이동 가능여부 판단)
    
    if ( 이동할 수 있는지(기사번호, 이동방향) ) 이동할 수 없다면 pass
    
    기사 위치 갱신 (tmp_knight_map -> knight_map)
    
    기사 정보(좌표, 피해량) 갱신

 

이동할 수 있는지 판단하는 함수를 재귀호출로 구현.

왕의 명령 -> 해당 기사의 이동 가능 여부 판단 -> 다른 기사가 있다면 -> 다른 기사의 이동 가능 여부 판단 -> ...

왕의 명령 -> 해당 기사의 이동 가능 여부 판단 -> 벽이 있다면 -> 재귀를 빠져나옴

 

[설명]

can_move_knight(기사 번호, 이동 방향)

기사의 이동 가능여부를 판단하고

이동이 가능한 기사에 한해 기사 이동

bool can_move_knight(int num, int dir) { // 기사번호, 방향
    KNIGHT* now = &knights[num];
    int ny = now->r + dy[dir];
    int nx = now->c + dx[dir];

    if ((ny < 1) || ((ny + now->h - 1) > L) || (nx < 1) || ((nx + now->w - 1) > L)) return false; // 영역을 벗어남

    FOR(y, ny, (ny + now->h)) {
        FOR(x, nx, (nx + now->w)) {
            if (map[y][x] == 2) return false; // 벽에 막힘
        }
    }

    FOR(y, ny, (ny + now->h)) {
        FOR(x, nx, (nx + now->w)) {
            if ((knight_map[y][x] != 0) && (knight_map[y][x] != num)) { // 자신이 아닌 다른 기사가 있음
                if (can_move_knight(knight_map[y][x], dir) == false) return false; // 다른 기사가 이동 못함
            }
        }
    }

    move_knight(num, dir); // 기사 이동

    return true;
}

 

update_knight_info(기사 번호)

기사가 이동한 위치, 피해량 갱신

void update_knight_info(int num) {
    bool visit[KNIGHTS_MAX] = { 0, };

    FOR(y, 1, (L + 1)) { // 좌표 갱신
        FOR(x, 1, (L + 1)) {
            int now = knight_map[y][x];
            if (now == 0) continue;
            if (visit[now] == true) continue;
            visit[now] = true;


            if (knights[now].k <= 0) continue; // 이미 체스판에서 사라진 기사
            if (knights[now].r != y || knights[now].c != x) knights[now].isMove = true; // 기사의 이동여부 플래그처리
            knights[now].r = y;
            knights[now].c = x;
        }
    }

    FOR(i, 1, (N + 1)) { // 피해계산
        KNIGHT* now = &knights[i];
        if (now->isMove == false) continue; // 이동한적 없는 기사라면
        now->isMove = false;
        if (i == num) continue; // 명령받은 기사
        if (now->k <= 0) continue; // 이미 체스판에서 사라진 기사

        int trap_cnt = 0;
        FOR(y, now->r, (now->r + now->h)) {
            FOR(x, now->c, (now->c + now->w)) {
                if (map[y][x] == 1) ++trap_cnt;
            }
        }

        now->k -= trap_cnt;
        now->damage_sum += trap_cnt;

        if (now->k <= 0) { // 체력이 0이하가 되면 체스판에서 사라짐
            FOR(y, now->r, (now->r + now->h)) {
                FOR(x, now->c, (now->c + now->w)) {
                    knight_map[y][x] = 0;
                }
            }
        }
    }
}

 

[회고]

테스트케이스 3번에서 막혔었습니다.

test_case_3.txt
0.00MB

 

문제 원인)

위 테스트 케이스에서 23번째 왕의 명령에서 오류를 발견함.

2번 기사에게 이동을 명령하고 2번기사만 이동을 하였는데, 

1번기사도 이동했다고 플래그처리가 있었음.

 

해결 방법)

피해량을 계산하는 로직을 처리하던 중

 

기존에

왕의 명령받은 기사인지, 체스판에서 사라진 기사인지 판별 후

isMove변수로 이동여부를 확인해 이동했던 기사인지 확인하고 isMove를 다시 false로 초기화해주었었습니다.

if (i == num) continue; // 명령받은 기사
if (now->k <= 0) continue; // 이미 체스판에서 사라진 기사
if (now->isMove == false) continue; // 이동한적 없는 기사라면
now->isMove = false;

 

이렇게 처리하니

선행조건에서 판별되어버려

이전의 기록이 초기화되지 않은채 다음 로직으로 넘어가 발생하는 문제였습니다.

그래서 조건의 판별 순서를 바꿔

isMove변수로 이동여부를 확인해 이동했던 기사인지 확인하고 isMove를 다시 false로 초기화 한 후,

왕의 명령을 받은 기사인지, 체스판에서 사라진 기사인지 판별해 주었습니다.

 

참고) 전체코드 중 Line number (118 ~ 121)

if (now->isMove == false) continue; // 이동한적 없는 기사라면
now->isMove = false;
if (i == num) continue; // 명령받은 기사
if (now->k <= 0) continue; // 이미 체스판에서 사라진 기사

[코드]

#include <iostream>
#include <cstring>
using namespace std;

#define FOR(i,s,e) for(int i=s; i<e; ++i)
#define L_MAX (40 + 1)
#define KNIGHTS_MAX (30 + 1)
#define KING_CMD_NUM 100

int L, N, Q;
int map[L_MAX][L_MAX]; // 지도 ( 0: 빈칸, 1: 함정, 2: 벽 )
int knight_map[L_MAX][L_MAX]; // 기사배치
int tmp_knight_map[L_MAX][L_MAX]; // 기사위치를 백업해둘 변수
struct KNIGHT {
    int r, c, h, w, k;
    int damage_sum; // 피해의 합을 기록
    bool isMove; // 기사가 해당 턴에 밀렸었는지 기록
};
KNIGHT knights[KNIGHTS_MAX]; // 기사들의 정보 저장
struct KING {
    int i, d;
};
KING commands[KING_CMD_NUM]; // 왕의 명령들 저장
int dy[4] = { -1,0,1,0 };
int dx[4] = { 0,1,0,-1 };

void input() {
    cin >> L >> N >> Q;
    FOR(y, 1, (L + 1)) {
        FOR(x, 1, (L + 1)) {
            cin >> map[y][x];
        }
    }
    FOR(i, 1, (N + 1)) {
        KNIGHT* now = &knights[i];
        cin >> now->r >> now->c >> now->h >> now->w >> now->k;

        FOR(y, now->r, (now->r + now->h)) {
            FOR(x, now->c, (now->c + now->w)) {
                knight_map[y][x] = i;
            }
        }
    }
    FOR(i, 0, Q) {
        KING* now = &commands[i];
        cin >> now->i >> now->d;
    }
}

void move_knight(int num, int dir) {
    KNIGHT* now = &knights[num];
    int ny = now->r + dy[dir];
    int nx = now->c + dx[dir];

    FOR(y, now->r, (now->r + now->h)) {
        FOR(x, now->c, (now->c + now->w)) {
            if (tmp_knight_map[y][x] == num) {
                tmp_knight_map[y][x] = 0;
            }
        }
    }

    FOR(y, ny, (ny + now->h)) {
        FOR(x, nx, (nx + now->w)) {
            if (tmp_knight_map[y][x] == 0) {
                tmp_knight_map[y][x] = num;
            }
        }
    }
}

bool can_move_knight(int num, int dir) { // 기사번호, 방향
    KNIGHT* now = &knights[num];
    int ny = now->r + dy[dir];
    int nx = now->c + dx[dir];

    if ((ny < 1) || ((ny + now->h - 1) > L) || (nx < 1) || ((nx + now->w - 1) > L)) return false; // 영역을 벗어남

    FOR(y, ny, (ny + now->h)) {
        FOR(x, nx, (nx + now->w)) {
            if (map[y][x] == 2) return false; // 벽에 막힘
        }
    }

    FOR(y, ny, (ny + now->h)) {
        FOR(x, nx, (nx + now->w)) {
            if ((knight_map[y][x] != 0) && (knight_map[y][x] != num)) { // 자신이 아닌 다른 기사가 있음
                if (can_move_knight(knight_map[y][x], dir) == false) return false; // 다른 기사가 이동 못함
            }
        }
    }

    move_knight(num, dir); // 기사 이동

    return true;
}

void update_knight_info(int num) {
    bool visit[KNIGHTS_MAX] = { 0, };

    FOR(y, 1, (L + 1)) { // 좌표 갱신
        FOR(x, 1, (L + 1)) {
            int now = knight_map[y][x];
            if (now == 0) continue;
            if (visit[now] == true) continue;
            visit[now] = true;


            if (knights[now].k <= 0) continue; // 이미 체스판에서 사라진 기사
            if (knights[now].r != y || knights[now].c != x) knights[now].isMove = true; // 기사의 이동여부 플래그처리
            knights[now].r = y;
            knights[now].c = x;
        }
    }

    FOR(i, 1, (N + 1)) { // 피해계산
        KNIGHT* now = &knights[i];
        if (now->isMove == false) continue; // 이동한적 없는 기사라면
        now->isMove = false;
        if (i == num) continue; // 명령받은 기사
        if (now->k <= 0) continue; // 이미 체스판에서 사라진 기사

        int trap_cnt = 0;
        FOR(y, now->r, (now->r + now->h)) {
            FOR(x, now->c, (now->c + now->w)) {
                if (map[y][x] == 1) ++trap_cnt;
            }
        }

        now->k -= trap_cnt;
        now->damage_sum += trap_cnt;

        if (now->k <= 0) { // 체력이 0이하가 되면 체스판에서 사라짐
            FOR(y, now->r, (now->r + now->h)) {
                FOR(x, now->c, (now->c + now->w)) {
                    knight_map[y][x] = 0;
                }
            }
        }
    }
}

void sol() {
    FOR(cmd_num, 0, Q) {
        if (knights[commands[cmd_num].i].k <= 0) continue; // 체스판에서 사라진 기사
        memcpy(tmp_knight_map, knight_map, sizeof(knight_map)); // 백업
        if (can_move_knight(commands[cmd_num].i, commands[cmd_num].d) == false) {
            continue; // 명령 받은 기사가 움직일 수 있는지
        }
        memcpy(knight_map, tmp_knight_map, sizeof(knight_map)); // 갱신
        update_knight_info(commands[cmd_num].i);
    }

    int ans = 0;
    FOR(i, 1, (N + 1)) {
        KNIGHT* now = &knights[i];
        if (now->k <= 0) continue;

        ans += now->damage_sum;
    }

    cout << ans;
}

int main() {
    // 여기에 코드를 작성해주세요.
    freopen("input.txt", "r", stdin);

    input();
    sol();

    return 0;
}

 

[문제 출처]

https://www.codetree.ai/training-field/frequent-problems/problems/royal-knight-duel/description?page=1&pageSize=20

반응형

[아이디어]

중간에 삽질하느라 디버깅하는데 오래 걸리기도 하고,

 

지시문 #define에 대해서...

[들어가기 전] #define R_MAX 20 + 1 ~~~ int wall[R_MAX * 2][R_MAX * 2] 위 코드에서 R_MAX * 2는 어떤 정수일까요? 1. 42 2. 22 정답을 아시는 분은 뒤로 가셔도 좋습니다! ㅎㅎ 정답은 2번 입니다! 왜 이런 결과가 나

kansstory.tistory.com

 

복잡했지만 단순 구현 문제였습니다.

 

단지 BFS를 이용해

인접한 네 방향을 주로 탐색했다면,

한 쪽 방향으로 퍼지게끔

구현에 변형이 필요했습니다.

 

또한 각 칸 사이엔 벽이 존재할 수 있는데

벽의 정보는

map의 크기를 2배로 늘려

사이사이 벽의 위치에 대한 좌표를 구할 수 있게끔 설계했습니다.

 

 

[요약]

1. 온풍기에서 바람이 퍼지듯이 불고

2. 온도를 조절하며

3. 최외곽의 온도가 1이상이라면 1씩 감소시키고

4. 초콜릿을 드시면

됩니다.

 

 

[방법]

바람이 한 방향으로 퍼지면서 나아간다.

이 부분은 BFS탐색 기법을 활용해 구현했습니다.

저는 방향벡터로

int dy[5] = { 0,0,0,-1,1 }; // 동 서 북 남
int dx[5] = { 0,1,-1,0,0 };

를 사용했습니다.

 

위 그림처럼 온풍기의 바람 방향이 동쪽(1)일 때,

방향벡터 배열 1~4까지 탐색하면서

 

동쪽(1)일 때,

동쪽(1) 방향으로 벽의 유무를 확인한 뒤

벽이 없다면 이동하고

 

서쪽(2)은 지나치고

 

북쪽(3)이나 남쪽(4)일 때,

위, 아래에 벽의 유무를 확인한 뒤

벽이 없다면 이동하고

동쪽(1)의 벽의 유무를 확인한 뒤

벽이 없다면 이동하도록

구현했습니다.

 

 

벽 위치 저장

map배열 좌표에 근거해

(r, c, d) = {(5, 4, 0), (4, 4, 1), (5, 6, 0)}

입력을 받는다면,

벽의 위치는 wall배열에 따르면

(r, c) = {(8, 7), (7,8), (8,11)}

에 저장되어 있게 됩니다.

 

따라서,

map배열에서 r좌표가 x이라면

wall배열에서 r좌표는 (x * 2) -1 임이 성립됩니다.

map배열 좌표에서 r좌표가 x, x+1인

사이의 벽은

wall배열에서 (x * 2)임이 성립됩니다.

 

이를 가정하고 wall 배열을 생성시, 

map 배열 크기의 2배만큼 키워

벽의 좌표를 저장했습니다.

 

[설명]

blow_from_heater(온풍기 인덱스)

주사위를 이동시켰을 때,

주사위의 좌표, 방향, 전개도, 획득한 점수 등을

업데이트해주는 함수

void blow_from_heater(int num) {
	/*
	온풍기에서 바람이 분다.
	*/
	queue<pair<int, int>> q; // 탐색할 좌표를 순서대로 집어 넣음
	int heater_map[R_MAX][C_MAX] = { 0, }; // 바람이 불면서 몇도의 온도가 저장되는지 기록
	int used[R_MAX][C_MAX] = { 0, }; // 이미 바람이 지나갔는지 확인
	NODE* now_heater = &heater[num]; // heater배열에서 현재 온풍기에 대한 정보를 저장해둠

	// 온풍기에서 바람이 불기 시작하는 위치
	int sy = now_heater->y + dy[now_heater->d];
	int sx = now_heater->x + dx[now_heater->d];

	heater_map[sy][sx] = 5;
	used[sy][sx] = 1;
	q.push(make_pair(sy, sx));

    while (!q.empty()) {
        pair<int, int> now = q.front();
        q.pop();

        FOR(i, 1, 5) {
            if (now_heater->d == 1 && i == 2) continue; // 온풍기 방향이 동쪽이면 서쪽은 탐색안함
            else if (now_heater->d == 2 && i == 1) continue; // 온풍기 방향이 서쪽이면 동쪽은 탐색안함
            else if (now_heater->d == 3 && i == 4) continue; // 온풍기 방향이 북쪽이면 남쪽은 탐색안함
            else if (now_heater->d == 4 && i == 3) continue; // 온풍기 방향이 남쪽이면 북쪽은 탐색안함
            
            // 현재 위치에 대한 벽의 유무를 파악
            int ty = (now.first * 2) - 1 + dy[i];
            int tx = (now.second  * 2) - 1 + dx[i];
            if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue; // 범위를 벗어난 벽을 탐색하지 않는지 확인
            if (wall[ty][tx] == 1) continue; // 벽에 막혔다면 탐색안함

            // 다음 바람이 불 다음 위치 업데이트
            int ny = now.first + dy[i];
            int nx = now.second + dx[i];
            if (i != now_heater->d) {
            	/*
                    북쪽이나 남쪽이라면 바람이 불던 방향(동 or 서)으로 한번더 꺽어줘야함
                    동쪽이나 서쪽이라면 바람이 불던 방향(북 or 남)으로 한번더 꺽어줘야함
                */
                    
                // 현재 위치에 대한 벽의 유무파악
                ty = (ny * 2) - 1 + dy[now_heater->d];
                tx = (nx * 2) - 1 + dx[now_heater->d];
                if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue; // 범위를 벗어난 벽을 탐색하지 않는지 확인
                if (wall[ty][tx] == 1) continue; // 벽에 막혔다면

                // 다음 바람이 불 다음 위치 업데이트
                ny = ny + dy[now_heater->d];
                nx = nx + dx[now_heater->d];
            }

            if (ny < 1 || ny > R || nx < 1 || nx > C) continue; //맵 밖으로 바람이 불진 않는지 확인
            if (heater_map[ny][nx] != 0) continue; // 이미 바람이 불었던 위치가 아닌지 확인
            if (used[ny][nx] != 0) continue; // 이미 바람이 불었던 위치가 아닌지 확인

            if ((heater_map[now.first][now.second] - 1) < 1) continue; // 온도가 0이하라면 더이상 바람이 닿지 않음
            heater_map[ny][nx] = heater_map[now.first][now.second] - 1; // 다음 바람이 불 위치의 온도를 저장
            used[ny][nx] = 1; // 바람이 지나간 자리임을 표시
            q.push(make_pair(ny, nx));
        }
    }

	add_heaterMap_to_temper(heater_map);
}

 

add_heaterMap_to_temper(int heater_map[][C_MAX])

전체 온도를 기록해둔 temper맵에

온풍기에서 바람이 나온 상태의 온도를 기록해둔 heater_map맵의 값만큼

더해줌

void add_heaterMap_to_temper(int heater_map[][C_MAX]) {
	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			temper[y][x] += heater_map[y][x];
		}
	}
}

 

control_temper()

주위 온도를 살펴보며 온도를 조절해줌

void control_temper() {
	/*
	온도 조절
	*/
	int spread_temper[R_MAX][C_MAX] = { 0, }; // 온도 조절하기 위해 주변으로 퍼진 온도 값 저장
	int flow_out_temper[R_MAX][C_MAX] = { 0, }; // 온도 조절하기 위해 주변으로 퍼지기 위해 빠져나간 온도 값 저장

	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			spread_temper[y][x] = 0;
			flow_out_temper[y][x] = 0;
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			if (temper[y][x] != 0) {
				FOR(i, 1, 5) {
                	// 벽의 유무 확인
					int ty = (y * 2) - 1 + dy[i];
					int tx = (x * 2) - 1 + dx[i];
					if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
					if (wall[ty][tx] == 1) continue;

					// 온도가 빠져나갈 좌표 확인
					int ny = y + dy[i];
					int nx = x + dx[i];
					if (ny<1 || ny > R || nx < 1 || nx > C) continue;

					if (temper[y][x] > temper[ny][nx]) {
						int L = (temper[y][x] - temper[ny][nx]) >> 2; // (두칸의 온도차 / 4)

						spread_temper[ny][nx] += L;
						flow_out_temper[y][x] -= L;
					}
				}
				flow_out_temper[y][x] += temper[y][x];
			}
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			temper[y][x] = flow_out_temper[y][x] + spread_temper[y][x];
		}
	}
}

 

decrease_outside_temper()

최외곽 좌표에 온도가 1이상이면 1씩 감소

void decrease_outside_temper() {
	/*
	가장 바깥쪽 칸의 온도 1씩 감소
	*/
	FOR(x, 1, C) {
		if (temper[1][x] >= 1) {
			temper[1][x] -= 1;
		}
	}
	FOR(x, 2, (C + 1)) {
		if (temper[R][x] >= 1) {
			temper[R][x] -= 1;
		}
	}
	FOR(y, 2, (R + 1)) {
		if (temper[y][1] >= 1) {
			temper[y][1] -= 1;
		}
	}
	FOR(y, 1, R) {
		if (temper[y][C] >= 1) {
			temper[y][C] -= 1;
		}
	}
}

 

eat_chocolate()

초콜릿 먹기

void eat_chocolate() {
	/* 초콜릿 먹기 */
	++chocolate;
}

 

isReachTargetTemper()

조사할 위치가 목표 온도에 도달했는지

bool isReachTargetTemper() {
	/* 목표 온도에 도달 했는지 */
	FOR(i, 0, t_cnt) {
		if (temper[target[i].y][target[i].x] < K) {
			return false;
		}
	}
	return true;
}

 

 

 

[코드]

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

#define R_MAX (20 + 1)
#define C_MAX (20 + 1)
#define WALL_MAX 21
#define FOR(i,s,e) for(int i=s; i<e; ++i)

int R, C, K;
int map[R_MAX][C_MAX];
struct NODE {
	int y;
	int x;
	int d;
};
NODE heater[410];
NODE target[410];
int dy[5] = { 0,0,0,-1,1 }; // 동 서 북 남
int dx[5] = { 0,1,-1,0,0 };
int W;
int wall[R_MAX * 2][C_MAX * 2];
int temper[R_MAX][C_MAX];
int h_cnt;
int t_cnt;
int chocolate;

void add_heaterMap_to_temper(int heater_map[][C_MAX]) {
	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			temper[y][x] += heater_map[y][x];
		}
	}
}

void blow_from_heater(int num) {
	/*
	온풍기에서 바람이 분다.
	*/
	queue<pair<int, int>> q;
	int heater_map[R_MAX][C_MAX] = { 0, };
	int used[R_MAX][C_MAX] = { 0, };
	NODE* now_heater = &heater[num];

	int sy = now_heater->y + dy[now_heater->d];
	int sx = now_heater->x + dx[now_heater->d];

	heater_map[sy][sx] = 5;
	used[sy][sx] = 1;
	q.push(make_pair(sy, sx));

	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();

		FOR(i, 1, 5) {
			if (now_heater->d == 1 && i == 2) continue;
			else if (now_heater->d == 2 && i == 1) continue;
			else if (now_heater->d == 3 && i == 4) continue;
			else if (now_heater->d == 4 && i == 3) continue;

			int ty = (now.first * 2) - 1 + dy[i];
			int tx = (now.second  * 2) - 1 + dx[i];
			if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
			if (wall[ty][tx] == 1) continue; // 벽에 막혔다면

			int ny = now.first + dy[i];
			int nx = now.second + dx[i];
			if (i != now_heater->d) {
				ty = (ny * 2) - 1 + dy[now_heater->d];
				tx = (nx * 2) - 1 + dx[now_heater->d];
				if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
				if (wall[ty][tx] == 1) continue; // 벽에 막혔다면

				ny = ny + dy[now_heater->d];
				nx = nx + dx[now_heater->d];
			}

			if (ny < 1 || ny > R || nx < 1 || nx > C) continue;
			if (heater_map[ny][nx] != 0) continue;
			if (used[ny][nx] != 0) continue;

			if ((heater_map[now.first][now.second] - 1) < 1) continue;
			heater_map[ny][nx] = heater_map[now.first][now.second] - 1;
			used[ny][nx] = 1;
			q.push(make_pair(ny, nx));
		}
	}

	add_heaterMap_to_temper(heater_map);
}

void control_temper() {
	/*
	온도 조절
	*/
	int spread_temper[R_MAX][C_MAX] = { 0, };
	int flow_out_temper[R_MAX][C_MAX] = { 0, };

	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			spread_temper[y][x] = 0;
			flow_out_temper[y][x] = 0;
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			if (temper[y][x] != 0) {
				FOR(i, 1, 5) {
					int ty = (y * 2) - 1 + dy[i];
					int tx = (x * 2) - 1 + dx[i];
					if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
					if (wall[ty][tx] == 1) continue;

					int ny = y + dy[i];
					int nx = x + dx[i];
					if (ny<1 || ny > R || nx < 1 || nx > C) continue;

					if (temper[y][x] > temper[ny][nx]) {
						int L = (temper[y][x] - temper[ny][nx]) >> 2; // (두칸의 온도차 / 4)

						spread_temper[ny][nx] += L;
						flow_out_temper[y][x] -= L;
					}
				}
				flow_out_temper[y][x] += temper[y][x];
			}
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			temper[y][x] = flow_out_temper[y][x] + spread_temper[y][x];
		}
	}
}

void decrease_outside_temper() {
	/*
	가장 바깥쪽 칸의 온도 1씩 감소
	*/
	FOR(x, 1, C) {
		if (temper[1][x] >= 1) {
			temper[1][x] -= 1;
		}
	}
	FOR(x, 2, (C + 1)) {
		if (temper[R][x] >= 1) {
			temper[R][x] -= 1;
		}
	}
	FOR(y, 2, (R + 1)) {
		if (temper[y][1] >= 1) {
			temper[y][1] -= 1;
		}
	}
	FOR(y, 1, R) {
		if (temper[y][C] >= 1) {
			temper[y][C] -= 1;
		}
	}
}

void eat_chocolate() {
	/* 초콜릿 먹기 */
	++chocolate;
}

bool isReachTargetTemper() {
	/* 목표 온도에 도달 했는지 */
	FOR(i, 0, t_cnt) {
		if (temper[target[i].y][target[i].x] < K) {
			return false;
		}
	}
	return true;
}

void sol() {
	while (isReachTargetTemper() == false) {
		FOR(i, 0, h_cnt) {
			blow_from_heater(i);
		}
		control_temper();
		decrease_outside_temper();
		eat_chocolate();
		if (chocolate > 100) {
			chocolate = 101;
			break;
		}
	}
	cout << chocolate;
}

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

	freopen("input.txt", "r", stdin);
	cin >> R >> C >> K;
	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			cin >> map[y][x];
			if (map[y][x] == 5) {
				target[t_cnt].y = y;
				target[t_cnt++].x = x;
			}
			else if (map[y][x] != 0) {
				heater[h_cnt].y = y;
				heater[h_cnt].x = x;
				heater[h_cnt++].d = map[y][x];
			}
		}
	}
	cin >> W;
	FOR(i, 0, W) {
		int r = 0, c = 0, d = 0;
		cin >> r >> c >> d;
		int y = (r * 2) - 1;
		int x = (c * 2) - 1;
		if (d == 0) {
			wall[y - 1][x] = 1;
		}
		else if (d == 1) {
			wall[y][x + 1] = 1;
		}
	}

	sol();

	return 0;
}

 

[문제 출처]

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

반응형

'Problem Solving > 백준' 카테고리의 다른 글

[백준_23288] 주사위 굴리기 2 (C++)  (4) 2024.01.29

[아이디어]

주사위 굴릴 때마다 변하는 전개도는 단순 구현

BFS 탐색기법을 활용해

풀었습니다.

 

 

[요약]

주사위가 특정 조건에 따라 이동한다.

이동 후 도착한 칸에 대한 점수를 획득한다.

다음 이동 방향을 결정한다.

 

 

[방법]

주사위를 굴릴때마다 주사위 전개도의 눈의 위치가 업데이트되도록 구현했습니다.

예를 들어,

주사위를 동쪽으로 굴렸을 때

 

주사위 눈금 2의 좌표(row, col)를 (0, 1)라고 한다면

(3, 1) 과 (1, 3)의 좌표의 값이 변화가 있다면 같도록 갱신해주어야 합니다.

 

위 그림에선 동쪽으로 주사위를 굴렸기에

1행 = {4, 1, 3, 6}이 오른쪽으로 한 칸씩 밀려

{6, 4, 1, 3}으로 변하게 됩니다.

이후 좌표 (3, 1)의 값을 (1, 3)의 값과 같도록 업데이트해주었습니다.

(((문제에선 좌표 (1, 1) 주사위의 '윗면'에 해당합니다.)))

 

만약, 남쪽이나 북쪽으로 주사위가 이동한다면

역시 아래쪽이나 위로 한 칸씩 1열에 해당하는 원소를 밀고,

(1, 3)의 값을 (3, 1)의 값과 같도록 업데이티 해주면 됩니다.

 

저의 경우에는 vector라이브러리를 사용한다면

일반 배열을 사용할 때보다 코드 작성이 간단하기에

vector라이브러리 채택해 사용했습니다.

void roll_dice() {
	if (dice.dir == 0 || dice.dir == 2) {
		if (dice.dir == 0) { // 주사위를 동쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[1] = tmp;
		}
		else if (dice.dir == 2) { // 주사위를 서쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[1] = tmp;
		}
		dice.dice_num[3][1] = dice.dice_num[1][3]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
	else if (dice.dir == 1 || dice.dir == 3) {
		if (dice.dir == 1) { // 주사위를 남쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		else if (dice.dir == 3) { // 주사위를 북쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		dice.dice_num[1][3] = dice.dice_num[3][1]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
}

 

나머지는 주사위 이동 후

BFS탐색 기법을 활용해

점수 계산과 주사위의 다음 방향 설정을 해주면 끝입니다.

 

 

[설명]

move_dice()

주사위를 이동시켰을 때,

주사위의 좌표, 방향, 전개도, 획득한 점수 등을

업데이트해주는 함수

void move_dice() {
	/*
	주사위를 이동시켰을 때,
	주사위의 좌표, 방향, 전개도, 획득한 점수 등을
	업데이트 해주는 함수
	*/
	if (isMove() == false) {
		// 주사위가 이동할 수 없는 방향이라면 반대 방향으로 바꿔줌
		if (dice.dir == 0) dice.dir = 2;
		else if (dice.dir == 2) dice.dir = 0;
		else if (dice.dir == 1) dice.dir = 3;
		else if (dice.dir == 3) dice.dir = 1;
	}

	// 주사위가 이동한 좌표 업데이트
	dice.y = dice.y + dy[dice.dir];
	dice.x = dice.x + dx[dice.dir];

	// 주사위를 굴렸을 때 바뀐 전개도 업데이트
	roll_dice();

	// 주사위가 획득한 점수 계산
	int s = bfs();
	score += (map[dice.y][dice.x] * s);

	// A는 주사위의 '아랫면'의 눈의 값을 저장
	// B는 지도상에서 주사위가 이동한 위치의 정수 값
	int A = dice.dice_num[3][1];
	int B = map[dice.y][dice.x];

	if (A > B) { // A > B 라면 시계 방향으로 90도 회전
		dice.dir += 1;
		if (dice.dir >= 4) dice.dir = 0;
	}
	else if (A < B) { // A < B 라면 반시계 방향으로 90도 회전
		dice.dir -= 1;
		if (dice.dir < 0) dice.dir = 3;
	}
}

 

isMove()

주사위가 이미 지정된 방향으로

이동이 가능한지 여부를 확인하는 함수

bool isMove() {
	/*
	주사위가 설정한 방향으로 이동이 가능한지 확인

	가능하다면 true
	불가능하다면 false
	*/
	int ny = dice.y + dy[dice.dir];
	int nx = dice.x + dx[dice.dir];

	if (ny < 1 || ny > N || nx < 1 || nx > M) return false;

	return true;
}

 

roll_dice()

주사위를 굴리면서 변화하는 전개도를 업데이트 해주는 함수

void roll_dice() {
	if (dice.dir == 0 || dice.dir == 2) {
		if (dice.dir == 0) { // 주사위를 동쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[1] = tmp;
		}
		else if (dice.dir == 2) { // 주사위를 서쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[1] = tmp;
		}
		dice.dice_num[3][1] = dice.dice_num[1][3]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
	else if (dice.dir == 1 || dice.dir == 3) {
		if (dice.dir == 1) { // 주사위를 남쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		else if (dice.dir == 3) { // 주사위를 북쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		dice.dice_num[1][3] = dice.dice_num[3][1]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
}

 

bfs()

지도상의 주사위가 이동한 위치의 정수 값과

주사위 위치의 동서남북으로 인접한 위치의 정수 값이

일치하는 좌표의 개수를 반환해 주는 함수

int bfs() {
	/*
	지도상에서 주사위가 이동한 위치의 정수 값과 
	동서남북으로 인접한 좌표에 일치하는 값이 있는지 탐색

	val은 지도상에서 주사위가 이동한 위치의 정수 값 저장
	cnt는 동일한 값이 인접한 좌표에 몇개가 존재하는지 찾음
	*/
	queue<pair<int, int>> q;
	int used[N_MAX][M_MAX] = { 0, };

	int val = map[dice.y][dice.x];
	int cnt = 1;

	q.push(make_pair(dice.y, dice.x));
	used[dice.y][dice.x] = 1;

	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();

		FOR(i, 0, 4) {
			int ny = now.first + dy[i];
			int nx = now.second + dx[i];

			if (ny<1 || ny>N || nx<1 || nx>M) continue;
			if (used[ny][nx] == 1) continue;
			if (map[ny][nx] != val)continue;

			used[ny][nx] = 1;
			++cnt;
			q.push(make_pair(ny, nx));
		}
	}

	return cnt;
}

 

 

 

[코드]

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

#define N_MAX 20 + 1
#define M_MAX 20 + 1
#define FOR(i,s,e) for(int i=s; i<e; ++i)

struct DICE { // 주사위의 정보를 담은 구조체
	int y;
	int x;
	int dir;
	vector<vector<int>> dice_num{ // 주사위 전개도
		{0, 2, 0, 0},
		{ 4,1,3,6 },
		{ 0,5,0,0 },
		{ 0,6,0,0 }
	};
};
int N, M, K;
int map[N_MAX][M_MAX];
int dy[4] = { 0,1,0,-1 }; // 동 남 북 서
int dx[4] = { 1,0,-1,0 };
DICE dice;
int score;

void init_dice() {
	dice.y = 1;
	dice.x = 1;
	dice.dir = 0; // 시작은 동쪽
}

bool isMove() {
	/*
	주사위가 설정한 방향으로 이동이 가능한지 확인

	가능하다면 true
	불가능하다면 false
	*/
	int ny = dice.y + dy[dice.dir];
	int nx = dice.x + dx[dice.dir];

	if (ny < 1 || ny > N || nx < 1 || nx > M) return false;

	return true;
}

void roll_dice() {
	if (dice.dir == 0 || dice.dir == 2) {
		if (dice.dir == 0) { // 주사위를 동쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[1] = tmp;
		}
		else if (dice.dir == 2) { // 주사위를 서쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[1] = tmp;
		}
		dice.dice_num[3][1] = dice.dice_num[1][3]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
	else if (dice.dir == 1 || dice.dir == 3) {
		if (dice.dir == 1) { // 주사위를 남쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		else if (dice.dir == 3) { // 주사위를 북쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		dice.dice_num[1][3] = dice.dice_num[3][1]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
}

int bfs() {
	/*
	지도상에서 주사위가 이동한 위치의 정수 값과 
	동서남북으로 인접한 좌표에 일치하는 값이 있는지 탐색

	val은 지도상에서 주사위가 이동한 위치의 정수 값 저장
	cnt는 동일한 값이 인접한 좌표에 몇개가 존재하는지 찾음
	*/
	queue<pair<int, int>> q;
	int used[N_MAX][M_MAX] = { 0, };

	int val = map[dice.y][dice.x];
	int cnt = 1;

	q.push(make_pair(dice.y, dice.x));
	used[dice.y][dice.x] = 1;

	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();

		FOR(i, 0, 4) {
			int ny = now.first + dy[i];
			int nx = now.second + dx[i];

			if (ny<1 || ny>N || nx<1 || nx>M) continue;
			if (used[ny][nx] == 1) continue;
			if (map[ny][nx] != val)continue;

			used[ny][nx] = 1;
			++cnt;
			q.push(make_pair(ny, nx));
		}
	}

	return cnt;
}

void move_dice() {
	/*
	주사위를 이동시켰을 때,
	주사위의 좌표, 방향, 전개도, 획득한 점수 등을
	업데이트 해주는 함수
	*/
	if (isMove() == false) {
		// 주사위가 이동할 수 없는 방향이라면 반대 방향으로 바꿔줌
		if (dice.dir == 0) dice.dir = 2;
		else if (dice.dir == 2) dice.dir = 0;
		else if (dice.dir == 1) dice.dir = 3;
		else if (dice.dir == 3) dice.dir = 1;
	}

	// 주사위가 이동한 좌표 업데이트
	dice.y = dice.y + dy[dice.dir];
	dice.x = dice.x + dx[dice.dir];

	// 주사위를 굴렸을 때 바뀐 전개도 업데이트
	roll_dice();

	// 주사위가 획득한 점수 계산
	int s = bfs();
	score += (map[dice.y][dice.x] * s);

	// A는 주사위의 '아랫면'의 눈의 값을 저장
	// B는 지도상에서 주사위가 이동한 위치의 정수 값
	int A = dice.dice_num[3][1];
	int B = map[dice.y][dice.x];

	if (A > B) { // A > B 라면 시계 방향으로 90도 회전
		dice.dir += 1;
		if (dice.dir >= 4) dice.dir = 0;
	}
	else if (A < B) { // A < B 라면 반시계 방향으로 90도 회전
		dice.dir -= 1;
		if (dice.dir < 0) dice.dir = 3;
	}
}

void sol() {
	init_dice();
	FOR(i, 0, K) {
		move_dice();
	}
	cout << score;
}

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

	freopen("input.txt", "r", stdin);
	cin >> N >> M >> K;
	FOR(y, 1, (N + 1)) {
		FOR(x, 1, (M + 1)) {
			cin >> map[y][x];
		}
	}
	sol();

	return 0;
}

 

[문제 출처]

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

반응형

'Problem Solving > 백준' 카테고리의 다른 글

[백준_23289] 온풍기 안녕! (C++)  (2) 2024.02.03

[아이디어]

그냥 구현 하는 식으로 풀었습니다.

 

 

[요약]

카드를 조합해 나올 수 있는 소수 찾기

 

 

[방법]

문자열에서 각 자리를 분리해

0~9까지 정수를 추출 합니다.

 

카드 조합 중 나올 수 있는 가장 큰 수를 찾습니다.

 

1부터 방금 구한 가장 큰 수까지

소수를 판별합니다.

 

카드를 조합해

조합한 수가 소수인지 아닌지 판별해줍니다.

 

 

[설명]

dfs(모든 카드의 장수, 재귀 단계, 카드 조합을 통해 구한 수)

백트래킹을 사용해 모든 카드 조합을 찾음

 

vector에서 중복 값을 제거

//-----------------------------------------------------
// 중복 제거 처리
    
// 중복을 제거하기 위한 정렬을 한다.
sort(ans.begin(), ans.end());
// unique()는 중복된 값을 vector의 가장 뒤로 보내고
// 이러한 값을 보낸 시작 위치를 반환한다.
// erase()를 사용해 중복값이 존재하는 위치부터 끝까지 지워준다.
ans.erase(unique(ans.begin(), ans.end()),ans.end());
//-----------------------------------------------------

중복을 제거하기 위해 unique()를 사용합니다.

다만,

정렬되지 않은 vector에 대해 unique를 적용한다면 아무런 변화가 없습니다.

그렇기에

unique()를 적용하기 전에 vector를 먼저 정렬해주고

unique()를 적용하면 중복값이 제거되.....

.....

.....

지 않습니다.

그 이유는 중복값을 vector의 끝 부분에 보냈을 뿐

vector 끝 부분에 존재하기 때문입니다.

또한 unique()는 중복값을 보낸 시작위치를 가르킵니다.

 

그래서,

erase()함수를 통해 unique()가 가르킨 위치부터

마지막까지 지워주면

깔끔하게 중복값은 제거됩니다.

 

 

[코드]

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

using namespace std;

#define N_MAX 10000000
#define FOR(i,s,e) for(int i=s; i<e; ++i)

vector<int> v;
bool arr[N_MAX];
int num;
vector<int> ans;
vector<int> used;

void dfs(int max_lv, int lv, int n) {
    if (max_lv == lv) {
        if (arr[n] == true)  ans.push_back(n);
        return;
    }
    FOR(i, 0, v.size()) {
        if (used[i] == 1) continue;
        used[i] = 1;
        n = (n * 10) + v[i];
        dfs(max_lv, lv + 1, n);
        n = (n / 10);
        used[i] = 0;
    }
}

int solution(string numbers) {
    int answer = 0;

    //-----------------------------------------------------
    //문자열에서 각 숫자 카드를 '한장 씩' 추출
    FOR(i, 0, numbers.length()) {
        v.push_back(int(numbers[i] - '0'));
    }
    //-----------------------------------------------------


    //-----------------------------------------------------
    //가장 큰 수를 확인하기 위해 내림차순으로 정렬
    sort(v.begin(), v.end(), greater<int>());

    // num변수에 가장 큰 수 저장
    FOR(i, 0, v.size()) {
        num = (num * 10) + v[i];
    }
    //-----------------------------------------------------

    
    //-----------------------------------------------------
    // 소수확인 후 판별하기 위한 arr배열 초기화
    // 소수라면 true, 아니라면 false
    memset(arr, true, sizeof(arr));
    
    // 0과 1은 소수가 아니다.
    arr[0] = false;
    arr[1] = false;

    // 가장 큰 수 9999999이므로 배열의 크기는 10000000
    // 카드 조합 중 나올 수 있는 가장 큰 수(num)까지 소수 판별을 한다.
    FOR(i, 1, 10000000) {
        if (arr[i] == false) continue;
        int s = i;
        for (int j = i * 2; j <= num; j += i) {
            arr[j] = false;
        }
    }
    //-----------------------------------------------------

    //-----------------------------------------------------
    // 카드를 조합해 나올 수 있는 모든 수를 구한다.
    FOR(i, 1, v.size() + 1) {
        used = vector<int>(v.size(), 0);
        dfs(i, 0, 0);
    }
    //-----------------------------------------------------

    
    //-----------------------------------------------------
    // 중복 제거 처리
    
    // 중복을 제거하기 위한 정렬을 한다.
    sort(ans.begin(), ans.end());
    // unique()는 중복된 값을 vector의 가장 뒤로 보내고
    // 이러한 값을 보낸 시작 위치를 반환한다.
    // erase()를 사용해 중복값이 존재하는 위치부터 끝까지 지워준다.
    ans.erase(unique(ans.begin(), ans.end()),ans.end());
    //-----------------------------------------------------

    answer = ans.size();
    return answer;
}

 

[문제 출처]

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

 

프로그래머스

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

programmers.co.kr

 

반응형

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

[프로그래머스] 카펫 (C++)  (0) 2024.01.26

[아이디어]

그냥 구현 하는 식으로 풀었습니다.

 

 

[요약]

입력 받은 yellow 블록 수에 알맞은 사각형의 형태를 만들어

이 사각형을 brown 블록으로 감쌀 수 있는지 없는지 알아보는 문제였습니다.

 

 

[방법]

yellow 블록 수는 사각형의 넓이 입니다.

yellow의 블록 수의 모든 약수를 구한다면 x개의 사각형에 대한 가로 세로 길이를 구할 수 있습니다.

예)

yellow 블록 수 = 24

24의 약수는 {1,2,3,4,6,8,12,24}

가로 길이가 24라면 세로 길이는 1,

가로 길이가 12라면 세로 길이는 2,

...

 

이렇게 구한 x개의 사각형에 대해

가로 길이의 2배

+

세로 길이의 2배

+

모퉁이 수 4개

=

brown 블록 수

의 식을 세울 수 있었습니다.

 

따라서,

yellow의 수에 대한 약수의 한 쌍을

각각 가로 세로 길이로 하여

brown 블록 수와 비교해주면 끝.

 

 

[설명]

get_divisor()

이 함수는 파라미터 num에 대한 약수를 찾는 함수입니다.

이렇게 찾은 약수를 각각 length_v, width_v 벡터에 추가해 줍니다.

 

isSame(가로, 세로, brown블록 수)

이 함수는 위에서 언급한 식에 대해

brown값과 비교해

같은지 다른지 판별하는 식입니다.

 

 

[코드]

#include <string>
#include <vector>

using namespace std;

#define FOR(i,s,e) for(int i=s; i<e; ++i)

vector<int> length_v; // 약수 저장
vector<int> width_v; // 약수 저장

void get_divisor(int num) {
    /*
    num에 대한 약수를 찾는 함수
    */
    FOR(i, 1, num + 1) {
        if ((num % i) != 0) continue; // 나누어 떨어지지 않으면 약수가 아님
        if (i > (num / i)) break; // 세로 길이가 가로 길이보다 길면 안됨
        length_v.push_back(i);
        width_v.push_back(num / i);
    }
}

bool isSame(int n1, int n2, int brown) {
    /*
    yellow를 감싸고 있는 brown의 크기는 항상
    yellow의 가로길이의 2배, 세로길이의 2배에 모퉁이 4개를 더한 값이다.
    */
    int width = n2 * 2;
    int length = n1 * 2;
    int corner = 4;

    /*
    num은 yellow의 크기에 대해 테두리를 감싼 크기
    */
    int num = width + length + corner;

    if (num == brown) return true; // brown과 같다면 찾고자 하는 가로 세로 길이임
    else return false;
}

vector<int> solution(int brown, int yellow) {
    vector<int> answer;

    get_divisor(yellow);

    FOR(i, 0, length_v.size()) {
        if (isSame(length_v[i], width_v[i], brown) == true) {
            answer.push_back(width_v[i] + 2);
            answer.push_back(length_v[i] + 2);
        }
    }

    return answer;
}

 

[문제 링크]

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

 

프로그래머스

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

programmers.co.kr

 

반응형

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

[프로그래머스] 소수 찾기 (C++)  (2) 2024.01.26

+ Recent posts