반응형

문제 링크: https://www.acmicpc.net/problem/1890

 

1890번: 점프

첫째 줄에 게임 판의 크기 N (4 ≤ N ≤ 100)이 주어진다. 그 다음 N개 줄에는 각 칸에 적혀져 있는 수가 N개씩 주어진다. 칸에 적혀있는 수는 0보다 크거나 같고, 9보다 작거나 같은 정수이며, 가장

www.acmicpc.net

<문제 풀이> 다이나믹 프로그래밍(DP) 알고리즘

 

dp식을 아래와 같이 정의할 수 있습니다.

dp[i][j] : 가장 왼쪽 위 칸에서 시작해서 (i, j)까지 규칙에 맞게 이동할 수 있는 경로의 개수

 

(1, 1)은 처음 시작 위치이므로 (1,1)까지 이동할 수 있는 경로의 개수는 1입니다.

-> dp[1][1] = 1

 

dp[1][1]부터 dp[i][j]까지 dp 값이 잘 계산되었다고 생각하면

(i, j)에서 남쪽방향, 동쪽 방향의 dp값은 다음과 같이 계산할 수 있습니다.

 

남쪽 방향 = dp[i + arr[i][[j]] += dp[i][j];

동쪽 방향 = dp[i][j + arr[i][j]] += dp[i][j];

 

(i, j)까지 이동할 수 있는 경로의 개수를 알고 있다고 했을 때 다음 좌표로 점프하는 것은 (i, j)까지의 경로의 개수와 같습니다. 점프는 다른 경로로 생각하는 것이 아니라 같은 경로 이기 때문입니다.

그러나 다음 좌표로 점프하는 (i, j)가 여러 개가 존재하므로 단순히 대입하는 것이 아니라 dp[nx][ny]에 누적해야 합니다.

dp[i][j]값들은 가장 왼쪽 위 칸에서 시작해서 차례로 계산을 하기 때문에 잘 계산되어 있습니다.

 

한 가지 주의할 점은 경로의 개수는 문제에서 2^63 -1보다 작거나 같다고 했으므로 long long 배열을 선언해야 합니다.

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<iostream>
using namespace std;
 
int arr[101][101];
long long dp[101][101]; //dp[i][j] :가장 왼쪽 위 칸에서 시작해서 (i, j)까지 규칙에 맞게 이동할 수 있는 경로의 개수
 
const int dx[2= { 10 };
const int dy[2= { 01 };
 
int main() {
    ios_base::sync_with_stdio(false);
    int n;
    cin >> n;
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> arr[i][j];
        }
    }
 
    dp[1][1= 1;
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (dp[i][j] == 0 || arr[i][j] == 0continue;
            for (int dir = 0; dir < 2; dir++) {
                int nx = i + dx[dir] * arr[i][j];
                int ny = j + dy[dir] * arr[i][j];
                if (nx < 1 || ny < 1 || nx > n || ny > n) continue;
                dp[nx][ny] += dp[i][j];
            }
 
        }
    }
    cout << dp[n][n];
 
    return 0;
}
cs
반응형
반응형

문제 링크: https://www.acmicpc.net/problem/1758

 

1758번: 알바생 강호

첫째 줄에 스타박스 앞에 서 있는 사람의 수 N이 주어진다. N은 100,000보다 작거나 같은 자연수이다. 둘째 줄부터 총 N개의 줄에 각 사람이 주려고 하는 팁이 주어진다. 팁은 100,000보다 작거나 같

www.acmicpc.net

<문제 풀이> 그리디, 정렬 알고리즘

 

팁 - (받은 등수 - 1)

 

위 식을 계산했을 때 0보다 작은 경우는 팁을 누적하지 않습니다.

전체 팁의 합을 최댓값으로 만드는 최적의 해는 사람을 배치할 때 팁을 적게 주는 사람을 최대한 뒤로 보내는 것입니다.

 

예를 들어

50원을 주는 사람이 1등이고, 10원을 주는 사람이 100등이라고 할 때 팁은 다음과 같이 계산됩니다.

 

50 - (1 - 1) = 50
10 -(100 -1 ) = 0

총 팁 : 50

 

50원을 주는 사람을 100등, 10원을 주는 사람을 1등이라고 할 때 팁은 다음과 같이 계산됩니다.

50 - (100 - 1) == 0

10 - (1 - 1) = 10

총 팁: 10

 

위 예시를 보면 뒤에 오는 사람은 식의 값이 0보다 작게 되어서 팁을 누적하지 않습니다.

둘 중에 한 명의 팁을 누적하지 않을 거라면 최대한 팁을 적게 주는 사람을 뒤로 보내 누적하지 않는 것이 최적의 해가 되는 것을 알 수 있습니다.

 

따라서 팁을 기준으로 내림차순 정렬한 뒤 식의 값을 누적하면 됩니다.

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
 
typedef long long ll;
 
int seq[100001];
 
int main() {
    ios_base::sync_with_stdio(false);
 
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> seq[i];
    }
    sort(seq, seq + n, greater<int>());
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        int m = seq[i] - i;
        if (m < 0continue;
        ans += m;
    }
    cout << ans;
    return 0;
}
cs
 

 

반응형
반응형

문제 링크: 

<문제 풀이> 그리디 알고리즘

 

현재까지 발견한 가격들 중 최소의 주유 가격으로 다음 최소 주유 가격을 발견할 때까지 이동하는 것을 반복하면 그것이 최적의 해가 됩니다.

 

위 그림에서

 

최솟값 5에서 다음 최솟값을 갱신할 때까지 이동하면 다음 위치는 2

최솟값 2에서 다음 최솟값을 갱신할 때까지 이동하면 다음 위치는 1

 

구현할 때는 바로 다음 위치까지 이동하는 것이 아닌 도시를 하나씩 방문하면 됩니다.

방문할 때마다 현재까지 발견한 가격들 중 최소의 주유 가격을 임시 변수에 저장하고 있어야 합니다.

현재 도시에서 다음 도시로 이동할 때 드는 비용은 (최솟값*거리)가 됩니다.

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long ll;
 
int n;
ll price[100000];
ll dist[100000];
int main() {
    ios_base::sync_with_stdio(false);
 
    cin >> n;
    for (int i = 0; i < n - 1; i++) {
        cin >> dist[i];
    }
    for (int i = 0; i < n; i++) {
        cin >> price[i];
    }
 
    ll ans = price[0* dist[0]; //처음에는 기름을 모두 넣고 다음 도시까지 간다.
    ll cur = price[0]; //현재까지 방문한 도시들의 주유 가격 중 최솟값
    for (int i = 1; i < n - 1; i++) {
        if (price[i] >= cur) { // 더 작은 주유 값을 발견하지 못하면
            ans += dist[i] * cur; // 현재까지 방문한 주유 최솟값으로 다음 도시로 이동
        }
        else { // 더 작은 주유 값을 발견하면
            cur = price[i]; // 최솟값 갱신
            ans += dist[i] * cur; // 최소 주유 값으로 다음 위치까지 이동
        }
    }
    cout << ans;
    return 0;
}
cs
 

 

반응형
반응형

문제 링크: https://www.acmicpc.net/problem/11055

 

11055번: 가장 큰 증가하는 부분 수열

수열 A가 주어졌을 때, 그 수열의 증가하는 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가하는

www.acmicpc.net

<문제 풀이> 다이나믹 프로그래밍(DP) 알고리즘

 

기본적인 LIS 알고리즘을 구현하면 되는 문제입니다.

 

dp는 아래와 같이 정의할 수 있습니다.

 

dp[i] : i 번째까지의 원소를 고려했을 때 가장 큰 증가하는 부분 수열 중에서 합이 가장 큰 것

 

dp[i]를 갱신하는 방법은 1부터 i -1까지의 dp[j]에 각각에 대해 i 번째 원소를 포함했을 때 그 합이 가장 큰 것으로 갱신해 주면 됩니다.

 

dp[1] + seq[i] : 1 번째까지의 원소를 고려했을 때 가장 큰 증가하는 부분 수열 중에서 합이 가장 큰 것 + i 번째 원소의 값

dp[2]  + seq[i]: 2 번째까지의 원소를 고려했을 때 가장 큰 증가하는 부분 수열 중에서 합이 가장 큰 것 + i 번째 원소의 값

.

.

dp[j] + seq[i] : j 번째까지의 원소를 고려했을 때 가장 큰 증가하는 부분 수열 중에서 합이 가장 큰 것

.

.

dp[i - 1] + seq[i] : i -1 번째까지의 원소를 고려했을 때 가장 큰 증가하는 부분 수열 중에서 합이 가장 큰 것

 

 

	for (int i = 1; i <= n; i++) {
		dp[i] = seq[i]; // 처음에는 자기 자신만 고려
		for (int j = 1; j < i; j++) {
			if (seq[i] <= seq[j]) continue;
			dp[i] = max(dp[i], dp[j] + seq[i]);
		}
	}

 

 

 

 

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
#include<algorithm>
using namespace std;
 
int n;
int dp[1001];
int seq[1001];
int main() {
    ios_base::sync_with_stdio(false);
 
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> seq[i];
    }
    for (int i = 1; i <= n; i++) {
        dp[i] = seq[i]; // 처음에는 자기 자신만 고려
        for (int j = 1; j < i; j++) {
            if (seq[i] <= seq[j]) continue;
            dp[i] = max(dp[i], dp[j] + seq[i]);
        }
    }
    cout << *max_element(dp, dp + n + 1);
    return 0;
}
cs
 

 

반응형

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준 1758번] 알바생 강호  (0) 2023.04.19
[백준 13305번] 주유소  (0) 2023.04.19
[백준 14916번] 거스름돈  (0) 2023.04.12
[백준 9465번] 스티커  (2) 2023.04.12
[백준 17626번] Four Squares  (0) 2023.04.12
반응형

문제 링크: https://www.acmicpc.net/problem/14916

 

14916번: 거스름돈

첫째 줄에 거스름돈 액수 n(1 ≤ n ≤ 100,000)이 주어진다.

www.acmicpc.net

<문제 풀이> 그리디 알고리즘

 

숫자가 큰 동전은 최대한으로 사용하고 숫자가 작은 동전은 최소한으로 사용하는 것이 최적의 해가 됩니다.

 

N이 5로 나누어 떨어질 때까지 N에서 2를 한 번씩 빼보는 것을 반복하면 됩니다.

 

N이 5로 나누어 떨어지면 그 몫은 5원 동전의 총 개수

N에서 2를 한 번 빼면 2원 동전 하나 사용

 

 

 

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
using namespace std;
 
int main(){
    ios_base::sync_with_stdio(false);
 
    int n;
    int ans = 0;
    cin >> n;
    while (n > 0) {
        if (n % 5 == 0) {
            ans += n / 5;
            n /= 5;
            break;
        }
        else {
            n -= 2;
            ans++;
        }
    }
    if (n < 0)cout << -1;
    else cout << ans;
    return 0;
}
cs
 

 

반응형
반응형

문제 링크: https://www.acmicpc.net/problem/9465

 

9465번: 스티커

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n (1 ≤ n ≤ 100,000)이 주어진다. 다음 두 줄에는 n개의 정수가 주어지며, 각 정수는 그 위치에 해당하는 스티커의

www.acmicpc.net

<문제 풀이> 다이나믹 프로그래밍

 

 

스티커를 위와 같이 2xN 배열로 표현했을 때 dp를 아래와 같이 정의할 수 있습니다.

 

dp[i][j] : 1열부터 j-1열까지 스티커를 떼어냈고 i행 j열에 있는 스티커를 떼어냈을 때 최솟값

 

N-1열까지 dp값이 잘 채워졌다고 가정하고 N열에 스티커를 뗄 수 있는 방법은 아래와 같은 4가지 경우의 수가 있습니다.

 

1. 1행 N열에 스티커를 떼어낼 때 2행 N-2열에 있는 스티커를 떼어낸 경우

2. 1행 N열에 스티커를 떼어낼 때 N-1열에 스티커를 아무것도 떼지 않은 경우

3. 2행 N열에 스티커를 떼어낼 때 1행 N-1열에 있는 스티커를 떼어낸 경우

4. 2행 N열에 스티커를 뗴어낼 때 N-1열에 스티커를 아무것도 떼지 않은 경우

N-1열에 아무것도 떼지 않는 경우는 N-2열에서 스티커를 떼어낸 경우입니다.

 

위 규칙들로 아래와 같은 dp 점화식을 만들 수 있습니다.

 

dp[1][1] = point[1][1];
dp[2][1] = point[2][1];
for (int i = 2; i <= n; i++) {
    dp[1][i] = max(dp[2][i - 1], dp[2][i - 2]) + point[1][i];
    dp[2][i] = max(dp[1][i - 1], dp[1][i - 2]) + point[2][i];
}
cout << max(dp[1][n], dp[2][n]) << '\n';

 

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
#include<algorithm>
using namespace std;
 
int n;
int point[3][100001];
int dp[3][100001];
int main(){
    ios_base::sync_with_stdio(false);
 
    int T; cin >> T;
    while (T--) {
        fill(&dp[0][0], &dp[2][100001], 0);
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> point[1][i];
        }
        for (int i = 1; i <= n; i++) {
            cin >> point[2][i];
        }
        dp[1][1= point[1][1];
        dp[2][1= point[2][1];
        for (int i = 2; i <= n; i++) {
            dp[1][i] = max(dp[2][i - 1], dp[2][i - 2]) + point[1][i];
            dp[2][i] = max(dp[1][i - 1], dp[1][i - 2]) + point[2][i];
        }
        cout << max(dp[1][n], dp[2][n]) << '\n';
    }
    return 0;
}
cs
 

 

반응형
반응형

문제 링크: https://www.acmicpc.net/problem/17626

 

17626번: Four Squares

라그랑주는 1770년에 모든 자연수는 넷 혹은 그 이하의 제곱수의 합으로 표현할 수 있다고 증명하였다. 어떤 자연수는 복수의 방법으로 표현된다. 예를 들면, 26은 52과 12의 합이다; 또한 42 + 32 + 1

www.acmicpc.net

<문제 풀이> 다이나믹 프로그래밍

 

dp[i] : 자연수 i를 제곱수 합으로 표현하는 최소 개수

 

i는 i보다 작거나 같은 제곱수 X와 i - X의 합으로 나타낼 수 있습니다.

 

dp[X] : 자연수 X를 제곱수 합으로 표현하는 최소 개수

dp[i - x] : 자연수 i - X를 제곱수 합으로 표현하는 최소 개수

 

따라서 dp[i] 는 i보다 작거나 같은 제곱수 X에 대하여 (dp[X] + dx[i-X] + 1)이 최소가 되는 값입니다.

 

ex) dp[10]

dp[0] = 0

dp[1] = 1

dp[10] = min(dp[10], dp[1] + dp[9] + 1)

dp[10] = min(dp[10], dp[4] + dp[6] + 1)

dp[10] = min(dp[10], dp[9] + dp[1] + 1)

 

 

<C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<algorithm>
using namespace std;
 
 
int dp[50001];
int main(int argc, char** argv){
    ios_base::sync_with_stdio(false);
    
    int n; cin >> n;
 
    fill(dp, dp + 500011e9);
    dp[0= 0;
    dp[1= 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j * j <= i; j++) {
            dp[i] = min(dp[i], dp[i - j * j] + 1);
        }
    }
    cout << dp[n];
 
    return 0;
}
cs
 

 

반응형

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준 14916번] 거스름돈  (0) 2023.04.12
[백준 9465번] 스티커  (2) 2023.04.12
[백준 23291번] 어항 정리  (0) 2023.04.08
[백준 21608번] 상어 초등학교  (0) 2023.04.08
[백준 23289번] 온풍기 안녕!  (0) 2023.04.06
반응형

문제 링크: https://www.acmicpc.net/problem/23291

 

23291번: 어항 정리

마법사 상어는 그동안 배운 마법을 이용해 어항을 정리하려고 한다. 어항은 정육면체 모양이고, 한 변의 길이는 모두 1이다. 상어가 가지고 있는 어항은 N개이고, 가장 처음에 어항은 일렬로 바

www.acmicpc.net

<문제 풀이> 구현

 

0. 어항 표현

N*N 배열을 만들고 N행을 어항의 맨 밑바닥으로 표현했습니다.

1. 물고기의 수가 가장 적은 어항에 물고기 한 마리 넣는다. 여러 개라면 모두에 한 마리씩 넣는다.

 

N행에 있는 원소들을 모두 순회하면서 최솟값을 찾으면 됩니다. 최솟값이 여러 개인 물고기를 모두 찾기 위해서 vector에 저장했습니다.

 

// 1. 물고기의 수가 가장 적은 어항에 물고기 한 마리 넣는다.
//    여러개라면 모두에 한 마리씩 넣는다.
void putFish() {
	vector<int> temp;
	int minValue = 1e9;
	for (int i = 1; i <= n; i++) {
		if (board[n][i] < minValue) {
			minValue = board[n][i];
			temp = {};
			temp.push_back(i);
		}
		else if (board[n][i] == minValue) {
			temp.push_back(i);
		}
	}
	for (auto index : temp) {
		board[n][index]++; // 물고기를 한 마리씩 넣는다.
	}
}

2. 어항을 쌓는다.

 

1) 먼저, 가장 왼쪽에 있는 어항을 그 오른쪽에 있는 어항의 위에 올려놓는다,

 

N행 1열에 있는 원소를 N - 1행 1열에 저장하고 N행을 왼쪽으로 한 칸씩 이동시키면 됩니다.

 

2) 2개 이상 쌓여있는 어항을 공중부양 시킨다.

 

2개 이상 쌓여있는 어항은 직사각형입니다. 이 직사각형을 다른 배열에 저장하면 공중 부양 시킨 것과 같은 효과를 낼 수 있습니다. 먼저 직사각형의 시작점과 끝점의 좌표를 먼저 구합니다.

 

먼저 시작 sx 행은 N행 1열부터 1행 1열까지 순회하면서 처음으로 0인 값이 나오는 행을 찾습니다.

0인 값이 나온 행 바로 이전 행이 시작 sx행이 됩니다.

(시작 sy 열은 항상 1)

 

그다음 끝 sy 열을 찾기 위해서 sx 행을 기준으로 1열부터 N열까지 순회하면서 처음으로 0인 값이 나오는 열을 찾습니다.

0인 값이 나온 열 바로 이전 열이 끝 ey열이 됩니다.

(끝 ex 열은 항상 n)

 

공중 부양시킬 직사각형의 시작점과 끝점을 가지고 직사각형을 저장할 배열을 하나 만들어서 저장해 줍니다. 배열에 저장한 것은 직사각형을 공중 부양 시킨 것과 같은 효과가 있습니다.

 

공중 부양 시킨 다음 어항을 회전하기 전에 한 가지 체크해야 할 게 있는데 회전을 했을 때 맨 밑바닥에 어항이 항상 있어야 합니다.

위 그림에서 공중 부양시킬 직사각형의 세로의 길이가, 공중 부양 시키고 난 다음 왼쪽으로 이동시킬 밑바닥의 길이보다 크면 회전을 했을 때 맨 밑바닥에 어항 없는 경우가 생깁니다.

따라서 이 경우에 회전을 진행하지 않도록 해주어야 합니다.

 

 

3) N행에 있는 배열을 맨 왼쪽으로 밀어 넣는다.

직사각형을 공중 부양시켰으면(배열에 저장했으면) N행에 있는 배열을 맨 왼쪽으로 밀어 넣으면 됩니다.

 

4) 공중 부양 배열을 90도 회전시킨다.

저장한 직사각형 배열을 90도 회전시킵니다.

 

5) 회전시킨 배열을 N - 1행에  쌓는다.

 

회전시킨 배열의 가장 밑에서부터 하나씩 순회하면서 N-1행부터 차곡차곡 쌓으면 됩니다.

 

 

// 2. 어항을 쌓는다.
void rotate() {
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			tempLevitation[i][j] = levitation[i][j];
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			levitation[j][n - i] = tempLevitation[i][j];;
		}
	}
}

void pile() {
	// 먼저, 가장 왼쪽에 있는 어항을 그 오른쪽에 있는 어항의 위에 올려 놓는다.
	board[n - 1][1] = board[n][1];
	for (int i = 2; i <= n; i++) {
		board[n][i - 1] = board[n][i]; // N행을 왼쪽으로 한 칸씩 이동
		board[n][i] = 0;
	}

	// 2개 이상 쌓여있는 어항을 공중부양 시킨다.
	int sx = 1, sy = 1, ex = n, ey = 1;
	while (true) {
		for (int i = n; i >= 1; i--) {
			if (board[i][1] == 0) {
				sx = i + 1;
				break;
			}
		}
		for (int i = 1; i <= n; i++) {
			if (board[sx][i] == 0) {
				ey = i - 1;
				break;
			}
		}
		int bottomCnt = 0;
		for (int i = ey + 1; i <= n; i++) {
			if (board[n][i]) {
				bottomCnt++;
			}
			else {
				break;
			}
		}
		if (ex - sx + 1 > bottomCnt) break; // 공중 부양 시킨 배열의 세로 길이가 맨 바닥의 길이보다 크면 더 이상 회전 X
		fill(&levitation[0][0], &levitation[100][101], 0);
		for (int i = sx; i <= ex; i++) {
			for (int j = sy; j <= ey; j++) {
				levitation[i - sx + 1][j - sy + 1] = board[i][j];
			}
		}

		// 배열의 맨 밑바닥을 맨 왼쪽으로 밀어 넣는다.
		int col = 1;
		for (int i = ey + 1; i <= n; i++) {
			if (board[n][i]) {
				board[n][col] = board[n][i];
				board[n][i] = 0;
				col++;
			}
		}
		rotate(); // 공중 부양 배열을 90도 회전시킨다.
		int row = n - 1; // 회전시킨 공중 부양 배열을 한 칸 위에 쌓는다.
		col = 1;
		for (int i = n; i >= 1; i--) {
			for (int j = 1; j <= n; j++) {
				if (levitation[i][j]) {
					board[row][col] = levitation[i][j];
					col++;
				}
			}
			if (col > 1) {
				col = 1;
				row--;
			}
		}
	}

}

 

3. 어항에 있는 물고기의 수를 조절한다.

 

물고기의 수 조절은 단순히 각 칸에 대해서 남, 동 방향으로 조절하면 됩니다. 동, 서, 남, 북을 모두 하지 않는 이유는 한 번 조절한 두 칸 사이에는 물고기의 수를 다시 조절하면 안 되기 때문입니다. 남, 동 방향으로만 물고기의 수를 조절하면 중복 없이 인접한 칸에 대해서 물고기의 수를 조절을 할 수 있습니다.

 

한 가지 주의할 점은 물고기의 수는 동시에 조절되기 때문에 임시 변수에 저장해 두었다가 실제 배열에 반영해야 합니다.

 

// 3. 어항에 있는 물고기의 수를 조절한다.
bool check(int x, int y) {
	return !(x < 1 || y < 1 || x > n || y > n || board[x][y] == 0);
}
void adjust() {
	init();
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (board[i][j] == 0) continue;
			if (check(i + 1, j)) {
				int d = abs(board[i][j] - board[i + 1][j]) / 5;
				if (d > 0 && board[i][j] > board[i + 1][j]) {
					tempAdjust[i][j] -= d;
					tempAdjust[i + 1][j] += d;
				}
				else if (d > 0 && board[i][j] < board[i + 1][j]) {
					tempAdjust[i][j] += d;
					tempAdjust[i + 1][j] -= d;
				}
			}
			if (check(i, j + 1)) {
				int d = abs(board[i][j] - board[i][j + 1]) / 5;
				if (d > 0 && board[i][j] > board[i][j + 1]) {
					tempAdjust[i][j] -= d;
					tempAdjust[i][j + 1] += d;
				}
				else if (d > 0 && board[i][j] < board[i][j + 1]) {
					tempAdjust[i][j] += d;
					tempAdjust[i][j + 1] -= d;
				}
			}


		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			board[i][j] += tempAdjust[i][j];
			if (board[i][j] < 0) board[i][j] = 0;
		}
	}
}

 

4. 이제 다시 어항을 바닥에 일렬로 놓는다.

 

문제 조건 그대로 구현하면 됩니다.

 

// 4. 이제 다시 어항을 바닥에 일렬로 놓는다.
void arrange() {
	int col = 1;
	init();
	for (int j = 1; j <= n; j++) {
		for (int i = n; i >= 1; i--) {
			if (board[i][j] == 0) continue;
			tempArrange[n][col] = board[i][j];
			col++;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			board[i][j] = tempArrange[i][j];
		}
	}
}

 

5. 새로운 공중 부양

2번에서 만들었던 공중 부양 코드를 참고해서 조금만 수정하면 만들 수 있습니다.

주의할 점 하나는 회전 후 어항을 왼쪽으로 밀어 넣을 때 2번에서는 맨 밑바닥만 하면 됐지만 5번에서는 밀어 넣어야 할 배열이 2개 이상입니다.

// 5. 새로운 공중 부양
void pile2() {
	int sx = n, sy = 1, ex = n, ey = n / 2;
	fill(&levitation[0][0], &levitation[100][101], 0);
	for (int i = sx; i <= ex; i++) {
		for (int j = sy; j <= ey; j++) {
			levitation[i - sx + 1][j - sy + 1] = board[i][j];
		}
	}

	int col = 1;
	for (int i = ey + 1; i <= n; i++) {
		if (board[n][i]) {
			board[n][col] = board[n][i];
			board[n][i] = 0;
			col++;
		}
	}
	rotate();
	rotate();
	int row = n - 1;
	col = 1;
	for (int i = n; i >= 1; i--) {
		for (int j = 1; j <= n; j++) {
			if (levitation[i][j]) {
				board[row][col] = levitation[i][j];
				col++;
			}
		}
		if (col > 1) {
			col = 1;
			row--;
		}
	}
}

void pile3() {
	int sx = n - 1, sy = 1, ex = n, ey = n / 4;
	fill(&levitation[0][0], &levitation[100][101], 0);
	for (int i = sx; i <= ex; i++) {
		for (int j = sy; j <= ey; j++) {
			levitation[i - sx + 1][j - sy + 1] = board[i][j];
		}
	}

	int col = 1;
	for (int i = ey + 1; i <= n; i++) {
		if (board[n][i]) {
			board[n][col] = board[n][i];
			board[n][i] = 0;
			col++;
		}
	}

	col = 1;
	for (int i = ey + 1; i <= n; i++) {
		if (board[n - 1][i]) {
			board[n - 1][col] = board[n - 1][i];
			board[n - 1][i] = 0;
			col++;
		}
	}
	rotate();
	rotate();
	int row = n - 2;
	col = 1;
	for (int i = n; i >= 1; i--) {
		for (int j = 1; j <= n; j++) {
			if (levitation[i][j]) {
				board[row][col] = levitation[i][j];
				col++;
			}
		}
		if (col > 1) {
			col = 1;
			row--;
		}
	}
}

 

 

 

<전체 C++소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
int n, k;
 
int board[101][101];
int levitation[101][101];
int tempLevitation[101][101];
int tempAdjust[101][101];
int tempArrange[101][101];
void input() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> board[n][i];
    }
}
 
void init() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            tempAdjust[i][j] = 0;
            tempArrange[i][j] = 0;
        }
    }
}
// 1. 물고기의 수가 가장 적은 어항에 물고기 한 마리 넣는다.
//    여러개라면 모두에 한 마리씩 넣는다.
void putFish() {
    vector<int> temp;
    int minValue = 1e9;
    for (int i = 1; i <= n; i++) {
        if (board[n][i] < minValue) {
            minValue = board[n][i];
            temp = {};
            temp.push_back(i);
        }
        else if (board[n][i] == minValue) {
            temp.push_back(i);
        }
    }
    for (auto index : temp) {
        board[n][index]++// 물고기를 한 마리씩 넣는다.
    }
}
 
// 2. 어항을 쌓는다.
void rotate() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            tempLevitation[i][j] = levitation[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            levitation[j][n - i] = tempLevitation[i][j];;
        }
    }
}
 
void pile() {
    // 먼저, 가장 왼쪽에 있는 어항을 그 오른쪽에 있는 어항의 위에 올려 놓는다.
    board[n - 1][1= board[n][1];
    for (int i = 2; i <= n; i++) {
        board[n][i - 1= board[n][i]; // N행을 왼쪽으로 한 칸씩 이동
        board[n][i] = 0;
    }
 
    // 2개 이상 쌓여있는 어항을 공중부양 시킨다.
    int sx = 1, sy = 1, ex = n, ey = 1;
    while (true) {
        for (int i = n; i >= 1; i--) {
            if (board[i][1== 0) {
                sx = i + 1;
                break;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (board[sx][i] == 0) {
                ey = i - 1;
                break;
            }
        }
        int bottomCnt = 0;
        for (int i = ey + 1; i <= n; i++) {
            if (board[n][i]) {
                bottomCnt++;
            }
            else {
                break;
            }
        }
        if (ex - sx + 1 > bottomCnt) break// 공중 부양 시킨 배열의 세로 길이가 맨 바닥의 길이보다 크면 더 이상 회전 X
        fill(&levitation[0][0], &levitation[100][101], 0);
        for (int i = sx; i <= ex; i++) {
            for (int j = sy; j <= ey; j++) {
                levitation[i - sx + 1][j - sy + 1= board[i][j];
            }
        }
 
        // 배열의 맨 밑바닥을 맨 왼쪽으로 밀어 넣는다.
        int col = 1;
        for (int i = ey + 1; i <= n; i++) {
            if (board[n][i]) {
                board[n][col] = board[n][i];
                board[n][i] = 0;
                col++;
            }
        }
        rotate(); // 공중 부양 배열을 90도 회전시킨다.
        int row = n - 1// 회전시킨 공중 부양 배열을 한 칸 위에 쌓는다.
        col = 1;
        for (int i = n; i >= 1; i--) {
            for (int j = 1; j <= n; j++) {
                if (levitation[i][j]) {
                    board[row][col] = levitation[i][j];
                    col++;
                }
            }
            if (col > 1) {
                col = 1;
                row--;
            }
        }
    }
 
}
 
 
// 3. 어항에 있는 물고기의 수를 조절한다.
bool check(int x, int y) {
    return !(x < 1 || y < 1 || x > n || y > n || board[x][y] == 0);
}
void adjust() {
    init();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (board[i][j] == 0continue;
            if (check(i + 1, j)) {
                int d = abs(board[i][j] - board[i + 1][j]) / 5;
                if (d > 0 && board[i][j] > board[i + 1][j]) {
                    tempAdjust[i][j] -= d;
                    tempAdjust[i + 1][j] += d;
                }
                else if (d > 0 && board[i][j] < board[i + 1][j]) {
                    tempAdjust[i][j] += d;
                    tempAdjust[i + 1][j] -= d;
                }
            }
            if (check(i, j + 1)) {
                int d = abs(board[i][j] - board[i][j + 1]) / 5;
                if (d > 0 && board[i][j] > board[i][j + 1]) {
                    tempAdjust[i][j] -= d;
                    tempAdjust[i][j + 1+= d;
                }
                else if (d > 0 && board[i][j] < board[i][j + 1]) {
                    tempAdjust[i][j] += d;
                    tempAdjust[i][j + 1-= d;
                }
            }
 
 
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            board[i][j] += tempAdjust[i][j];
            if (board[i][j] < 0) board[i][j] = 0;
        }
    }
}
 
// 4. 이제 다시 어항을 바닥에 일렬로 놓는다.
void arrange() {
    int col = 1;
    init();
    for (int j = 1; j <= n; j++) {
        for (int i = n; i >= 1; i--) {
            if (board[i][j] == 0continue;
            tempArrange[n][col] = board[i][j];
            col++;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            board[i][j] = tempArrange[i][j];
        }
    }
}
 
// 5. 새로운 공중 부양
void pile2() {
    int sx = n, sy = 1, ex = n, ey = n / 2;
    fill(&levitation[0][0], &levitation[100][101], 0);
    for (int i = sx; i <= ex; i++) {
        for (int j = sy; j <= ey; j++) {
            levitation[i - sx + 1][j - sy + 1= board[i][j];
        }
    }
 
    int col = 1;
    for (int i = ey + 1; i <= n; i++) {
        if (board[n][i]) {
            board[n][col] = board[n][i];
            board[n][i] = 0;
            col++;
        }
    }
    rotate();
    rotate();
    int row = n - 1;
    col = 1;
    for (int i = n; i >= 1; i--) {
        for (int j = 1; j <= n; j++) {
            if (levitation[i][j]) {
                board[row][col] = levitation[i][j];
                col++;
            }
        }
        if (col > 1) {
            col = 1;
            row--;
        }
    }
}
 
void pile3() {
    int sx = n - 1, sy = 1, ex = n, ey = n / 4;
    fill(&levitation[0][0], &levitation[100][101], 0);
    for (int i = sx; i <= ex; i++) {
        for (int j = sy; j <= ey; j++) {
            levitation[i - sx + 1][j - sy + 1= board[i][j];
        }
    }
 
    int col = 1;
    for (int i = ey + 1; i <= n; i++) {
        if (board[n][i]) {
            board[n][col] = board[n][i];
            board[n][i] = 0;
            col++;
        }
    }
 
    col = 1;
    for (int i = ey + 1; i <= n; i++) {
        if (board[n - 1][i]) {
            board[n - 1][col] = board[n - 1][i];
            board[n - 1][i] = 0;
            col++;
        }
    }
    rotate();
    rotate();
    int row = n - 2;
    col = 1;
    for (int i = n; i >= 1; i--) {
        for (int j = 1; j <= n; j++) {
            if (levitation[i][j]) {
                board[row][col] = levitation[i][j];
                col++;
            }
        }
        if (col > 1) {
            col = 1;
            row--;
        }
    }
}
 
void solve() {
    init();
    input();
    int ans = 0;
    while (*max_element(board[n] + 1, board[n] + n + 1- *min_element(board[n] + 1, board[n] + n + 1> k) {
        putFish();
        pile();
        adjust();
        arrange();
        pile2();
        pile3();
        adjust();
        arrange();
        ans++;
    }
    cout << ans;
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
 
    solve();
 
 
    return 0;
}
cs
 

 

반응형

+ Recent posts