Repository: AlgoLeadMe/AlgoLeadMe-15
Pull request #12: 3-mj010504
Author: login: mj010504
Labels: name: mj010504
name: 리뷰 완료 ✔️
Created: 2025-05-12T03:47:36Z | Merged: 2025-05-21T06:41:34Z

## 🔗 문제 링크
욕심쟁이 판다
https://www.acmicpc.net/problem/1937

## ✔️ 소요된 시간
1시간

## ✨ 수도 코드
- 판다는 **대나무가 작은곳에서 많은 곳으로만 이동 가능**합니다.
- 판다의 시작점을 임의로 결정하여 **최대한 많은 칸을 이동하기 위한 시작점**을 찾아야합니다.

### 첫번째 풀이
- 처음에는 시작점마다 **이동 가능한 최대 경로 수**를 구하기 위해 모든 시작점에서 DFS를 하는 방식으로 풀이했습니다.
- 하지만 이러한 방식으로는, **중복되는 이동 경로**가 많아지기 때문에 시간초과가 났습니다.

### 두번째 풀이
- 따라서 처음에는 DP라고 생각하기 보다는, **중복되는 이동 경로를 없애는 방법**을 생각해봤습니다.
- 저는 배열을 따로 만들어서(제 코드에서는 cv), 각 배열의 요소가 **시작점일 때 가능한 최대 방문 수**를 저장하여, **중복되는 이동 경로**를 없애도록 했습니다.
- 각 시작점에서 상/하/좌/우 방향으로 **대나무 수가 현재보다 많은 칸으로 이동 가능**할 때
1. 아직 최대 방문 수가 계산이 안된 칸이라면 -> 재귀적으로 함수를 호출하여 해당 칸에서 최대 방문 수 계산합니다.
2. 최대 방문수가 계산이 된 칸이라면 -> **중복으로 경로를 이동시킬 필요가 없으므로**, 현재 위치한 칸에서 최대 방문수를 계산해줍니다.(이 부분 수도코드로 설명이 힘드네요.. 로직넣을테니 참고부탁드립니다. 혹시 더 깔끔하게 말로 설명이 가능하신 분이라면 리뷰에 남겨주시면 감사하겠습니다.) 
<details>
<summary> 해당 부분 로직 </summary>

```cpp
if(xx >= 0 && xx < n && yy >=0 && yy < n && v[xx][yy] > value) {
                canMove = true;
                if(cv[xx][yy] == -1) {
                    cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
                }
                else {
                    cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);
                }
            }
```
</details>
- 만약 상/하/좌/우로 이동가능한 경로가 없다면 1을 반환해줍니다.
- 최대 방문 수 배열(cv)에서 최댓값을 계산하여 출력합니다.
 
## 📚 새롭게 알게된 내용
- 이 부분에서는 기존 대나무 수 배열에서 추가적으로 **각 시작점으로 부터 최대 방문수를 저장하는 배열이 필요하다는 걸 깨닫는 것**이 중요했던 것 같습니다.
- 수도 코드로 설명하는 것이 어려워서 많이 연습해야할 것 같습니다.



Diff:
diff --git a/mj010504/BOJ/1937.cpp b/mj010504/BOJ/1937.cpp
new file mode 100644
index 0000000..0959d34
--- /dev/null
+++ b/mj010504/BOJ/1937.cpp
@@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value) {
+    bool canMove = false;
+      for(int d = 0; d < 4; d++) {
+            int xx = x + dx[d];
+            int yy = y + dy[d];
+
+            if(xx >= 0 && xx < n && yy >=0 && yy < n && v[xx][yy] > value) {
+                canMove = true;
+                if(cv[xx][yy] == -1) {
+                    cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
+                }
+                else {
+                    cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);
+                }
+            }
+      }
+
+      if(!canMove) {
+        cv[x][y] = 1;
+        return 1;
+      }
+      else return cv[x][y];
+
+ }
+    
+
+
+int main() {
+  // freopen("input.txt", "rt", stdin);  
+    ios::sync_with_stdio(false);
+    cin.tie(0);
+    cout.tie(0);
+
+    cin >> n;
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) cin >> v[i][j];
+    }
+    
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) {
+            if(cv[i][j] == -1) movePanda(i, j, v[i][j], cv);
+        }
+    }
+
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) {
+            res = max(res, cv[i][j]);
+        }
+    }
+
+
+    cout << res;
+
+    return 0;
+}
+
diff --git a/mj010504/README.md b/mj010504/README.md
index 0eab288..f8c0d7c 100644
--- a/mj010504/README.md
+++ b/mj010504/README.md
@@ -3,4 +3,6 @@
  | 차시 |    날짜    | 문제유형 | 링크 | 풀이 |
  |:----:|:---------:|:----:|:-----:|:----:|
  | 1차시 | 2025.05.01 |  BFS/DFS  | [유기농 배추](https://www.acmicpc.net/problem/1012)|(https://github.com/AlgoLeadMe/AlgoLeadMe-15/pull/3)|
+ | 2차시 | 2025.05.01 |  DFS | [컴백홈](https://www.acmicpc.net/problem/1189)|(https://github.com/AlgoLeadMe/AlgoLeadMe-15/pull/3)|
+ | 3차시 | 2025.05.01 |  DP  | [욕심쟁이 판다](https://www.acmicpc.net/problem/1937)|(https://github.com/AlgoLeadMe/AlgoLeadMe-15/pull/3)|
  ---


Reviews:
user: login: hyeokbini
body: 풀이 잘 읽었습니다! DP랑 그래프 이론을 섞은 문제네요

저도 처음 문제를 읽어보고는 단순한 DFS 같은데 골드 3이라고? 라는 생각이 제일 먼저 들었는데
메모이제이션을 활용해서 최적화를 잘 해야 풀리는 문제였네요. 고생하셨습니다!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-12T11:38:32Z
user: login: mj010504
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T11:54:14Z
user: login: mj010504
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-12T11:55:34Z
user: login: 9kyo-hwang
body: 안녕하세요 :) 지금은 종료된 알고리드미 3팀 소속 구교황이라고 합니다.

모든 칸에 대해 그 칸에 대해서는 1칸 움직인 걸로 취급할 수 있으니, dp 배열의 값을 1로 초기화해 재귀 식을 더욱 간결하게 작성할 수 있습니다.

```cpp
const vector<pair<int, int>> Offset
{
    {-1, 0},
    {0, 1},
    {1, 0},
    {0, -1}
};

int N;
vector<vector<int>> Forest, DP;

bool OutOfBound(int x, int y)
{
    return x < 0 || x >= N || y < 0 || y >= N;
}

int DFS(int x, int y)
{
    if(DP[x][y] > 1)  // 1 초과는 다른 재귀 호출에 의해 이미 계산된 경우
    {
        return DP[x][y];
    }
    
    for(const auto& [dx, dy] : Offset)
    {
        int nx = x + dx, ny = y + dy;
        if(!OutOfBound(nx, ny) && Forest[nx][ny] > Forest[x][y])  // 조건에 충족한 인접칸 방문
        {
            DP[x][y] = max(DP[x][y], DFS(nx, ny) + 1);  // 그 중 가장 멀리 이동한 결과를 dp 배열에 갱신
        }
    }
    
    return DP[x][y];
}
```

직관적인 변수명, 깔끔한 코드 흐름에 대해 고민해보셔도 좋겠습니다 :) 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-18T07:47:32Z
user: login: mj010504
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-18T08:59:05Z
user: login: mj010504
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-18T09:01:43Z
user: login: flydongwoo
body: 풀이 잘 읽었습니다!
처음에 DFS로 접근했다가 중복 경로를 생각해 DP로 방향을 전환하는 과정을 넣어주셔서 저 역시 문제를 접근하는 방법을 더 효율적으로 생각할 수 있었던 것 같습니다!
골드 문제인지 모르고 봤다가 생각보다 복잡하고 따져줘야 할 것이 많네요 ,,,, 메모이제이션을 활용해야하는 문제였군요..
제가 궁금했던 부분들이랑 하고 싶은 얘기를 혁빈님이랑 교황님께서 잘 작성해주신 것 같습니다!
수고 많으셨습니다 :)
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-19T15:15:35Z
user: login: seobeeeee1001
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-21T05:24:49Z

Review comments:
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value, vector<vector<int>>& cv) {
path: mj010504/BOJ/1937.cpp
user: login: hyeokbini
body: cv 변수가 전역 변수로 지정되어 있는데 인자로 넘겨주신 특별한 이유가 있을까요?? 궁금합니다!
created_at: 2025-05-12T11:29:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value, vector<vector<int>>& cv) {
+    bool canMove = false;
+      for(int d = 0; d < 4; d++) {
+            int xx = x + dx[d];
+            int yy = y + dy[d];
+
+            if(xx >= 0 && xx < n && yy >=0 && yy < n && v[xx][yy] > value) {
+                canMove = true;
+                if(cv[xx][yy] == -1) {
+                    cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
+                }
+                else {
+                    cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);
+                }
path: mj010504/BOJ/1937.cpp
user: login: hyeokbini
body: 풀이에서 언급해주신 부분인데, 확실히 수도코드로는 설명이 힘들 것 같네요..

만약 **특정 지점에서 출발하는 모든 경우의 수가 계산이 끝나 있다면** 다른 지점에서 탐색을 시작해서 이 지점을 밟아야 한다면, **더 깊이 탐색하며 연산할 필요 없이 기존에 계산되어 있는 값을 재사용한다**라는 느낌인 것 같습니다..? 메모이제이션 느낌이네요
created_at: 2025-05-12T11:36:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value, vector<vector<int>>& cv) {
path: mj010504/BOJ/1937.cpp
user: login: mj010504
body: 아 vsCode에서 디버깅 한다고 추가하고 없애야 했는데 실수로 안없앴네요. 수정할게요.
created_at: 2025-05-12T11:54:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value, vector<vector<int>>& cv) {
+    bool canMove = false;
+      for(int d = 0; d < 4; d++) {
+            int xx = x + dx[d];
+            int yy = y + dy[d];
+
+            if(xx >= 0 && xx < n && yy >=0 && yy < n && v[xx][yy] > value) {
+                canMove = true;
+                if(cv[xx][yy] == -1) {
+                    cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
+                }
+                else {
+                    cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);
+                }
path: mj010504/BOJ/1937.cpp
user: login: mj010504
body: 넵 의견 감사합니다!
created_at: 2025-05-12T11:55:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
path: mj010504/BOJ/1937.cpp
user: login: 9kyo-hwang
body: vector를 사용한다면 입력받은 크기 n에 맞게 맞춰서 사용하는 게 좋다 생각됩니다.
```cpp
int main() {
  int n; cin >> n;
  v.resize(n, vector<int>(n));
  cv.assign(n, vector<int>(n, -1));
}
```
created_at: 2025-05-18T07:42:44Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value) {
+    bool canMove = false;
+      for(int d = 0; d < 4; d++) {
+            int xx = x + dx[d];
+            int yy = y + dy[d];
+
+            if(xx >= 0 && xx < n && yy >=0 && yy < n && v[xx][yy] > value) {
+                canMove = true;
+                if(cv[xx][yy] == -1) {
+                    cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
+                }
+                else {
+                    cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);
+                }
+            }
+      }
+
+      if(!canMove) {
+        cv[x][y] = 1;
+        return 1;
+      }
+      else return cv[x][y];
+
+ }
+    
+
+
+int main() {
+  // freopen("input.txt", "rt", stdin);  
+    ios::sync_with_stdio(false);
+    cin.tie(0);
+    cout.tie(0);
+
+    cin >> n;
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) cin >> v[i][j];
+    }
+    
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) {
+            if(cv[i][j] == -1) movePanda(i, j, v[i][j], cv);
+        }
+    }
+
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) {
+            res = max(res, cv[i][j]);
+        }
+    }
path: mj010504/BOJ/1937.cpp
user: login: 9kyo-hwang
body: movePanda 함수가 int 값을 반환하도록 설계하셨으니, movePanda 함수를 호출할 때마다 반환된 값을 즉시 갱신시켜도 괜찮을 것 같습니다.

```cpp
for(int i = 0; i < n; ++i) {
  for(int j = 0; j < n; ++j) {
    if(cv[i][j] == -1) {
      res = max(res, movePanda(i, j, v[i][j]));
    }
  }
}
```
created_at: 2025-05-18T07:44:54Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
path: mj010504/BOJ/1937.cpp
user: login: mj010504
body: resize란 함수를 처음 알았네요. 좋은 방법 알려주셔서 감사하고 다음 문제부터 적용하겠습니다!
created_at: 2025-05-18T08:59:05Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,72 @@
+#include <bits/stdc++.h>
+
+using namespace std;
+
+struct p {
+    int x, y, value, cnt;
+};
+
+int dx[4] = {0,0,1,-1};
+int dy[4] = {1,-1,0,0};
+
+int res = 0;
+int n;
+vector<vector<int>> v(500, vector<int> (500));
+vector<vector<int>> cv(500, vector<int> (500, -1)); // 시작점일 때 가능한 최대 방문 수
+
+int movePanda(int x, int y, int value) {
+    bool canMove = false;
+      for(int d = 0; d < 4; d++) {
+            int xx = x + dx[d];
+            int yy = y + dy[d];
+
+            if(xx >= 0 && xx < n && yy >=0 && yy < n && v[xx][yy] > value) {
+                canMove = true;
+                if(cv[xx][yy] == -1) {
+                    cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
+                }
+                else {
+                    cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);
+                }
+            }
+      }
+
+      if(!canMove) {
+        cv[x][y] = 1;
+        return 1;
+      }
+      else return cv[x][y];
+
+ }
+    
+
+
+int main() {
+  // freopen("input.txt", "rt", stdin);  
+    ios::sync_with_stdio(false);
+    cin.tie(0);
+    cout.tie(0);
+
+    cin >> n;
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) cin >> v[i][j];
+    }
+    
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) {
+            if(cv[i][j] == -1) movePanda(i, j, v[i][j], cv);
+        }
+    }
+
+    for(int i = 0; i < n; i++) {
+        for(int j = 0; j < n; j++) {
+            res = max(res, cv[i][j]);
+        }
+    }
path: mj010504/BOJ/1937.cpp
user: login: mj010504
body: 저도 평소엔 이런식으로 코드를 짰던거 같은데 이 문제에선 적용하지 않았었네요. 캐치해주셔서 감사합니다!
created_at: 2025-05-18T09:01:43Z
author_association: COLLABORATOR

PR comments:
user: login: mj010504
created_at: 2025-05-18T09:03:17Z
body: 직관적인 변수명, 깔끔한 코드 흐름 -> 항상 고민하는 부분이긴 한데 더 깔끔한 방식을 교황님꼐서 제시해주셨네요. 다음 풀이에 적용해보도록 하겠습니다~
author_association: COLLABORATOR
user: login: seobeeeee1001
created_at: 2025-05-21T05:21:54Z
body: 일반적인 dp가 아니라 dfs가 합쳐진 dp라 이해하기가 힘들었네요ㅠㅠ
제가 bottom-up, top-down 구분을 아직 못하는데 
약간 top-down 느낌인 것 같기도 합니다 (아닐 수도 있음;;)

코드에 대해서 제 나름대로 이해를 해보면
모든 칸에 대해서 해당 칸을 시작점으로 했을 때의 최대 이동 거리를 재귀를 통해서 구하신 것 같습니다
movePanda는 받은 인덱스를 시작점으로 했을 때의 최대 이동 거리를 구하는 함수인 것 같구요.
상하좌우를 살펴보면서 계산된게 있다면 해당 칸으로 움직였을 때의 최대 이동 거리와 그렇지 않았을 때의 이동거리를 살펴보면서 최댓값을 구하는 것 같습니다. 
계산 되지 않았다면 movePanda를 재귀호출해서 해당 칸의 최대 이동 거리를 구하고요. 

그리고 cv 배열에 최대 이동거리가 저장되기 때문에 중복되는 부분이 없어지는 것 같네요

cv[x][y] = max(cv[x][y], 1 + movePanda(xx, yy, v[xx][yy], cv));
cv[x][y] = max(cv[x][y], 1 + cv[xx][yy]);

이 두 가지가 점화식인 것 같은데 계산되는 순서가 역순이라 조금 헷갈렸던 것 같습니다.
저는 순차적으로 계산되는 dp만 경험해봐서 살짝 벽이 느껴졌네요
그리고 완전히 이해한 건 아닌 것 같아서 조금 더 공부를 해봐야겠습니다 

암튼 고생하셨습니다!


author_association: COLLABORATOR