Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

19-yuyu0830 #71

Merged
merged 2 commits into from
Jul 25, 2024
Merged

19-yuyu0830 #71

merged 2 commits into from
Jul 25, 2024

Conversation

yuyu0830
Copy link
Collaborator

@yuyu0830 yuyu0830 commented Jul 12, 2024

image
드디어 상위 100문을 골드로 채웠습니다! 다음은 플레..?

🔗 문제 링크

우수 마을

✔️ 소요된 시간

4 시간

✨ 수도 코드

❓ 문제


N개의 마을로 이루어진 나라가 있다. 편의상 마을에는 1부터 N까지 번호가 붙어 있다고 하자. 이 나라는 트리(Tree) 구조로 이루어져 있다. 즉 마을과 마을 사이를 직접 잇는 N-1개의 길이 있으며, 각 길은 방향성이 없어서 A번 마을에서 B번 마을로 갈 수 있다면 B번 마을에서 A번 마을로 갈 수 있다. 또, 모든 마을은 연결되어 있다. 두 마을 사이에 직접 잇는 길이 있을 때, 두 마을이 인접해 있다고 한다.

이 나라의 주민들에게 성취감을 높여 주기 위해, 다음 세 가지 조건을 만족하면서 N개의 마을 중 몇 개의 마을을 '우수 마을'로 선정하려고 한다.

'우수 마을'로 선정된 마을 주민 수의 총 합을 최대로 해야 한다.
마을 사이의 충돌을 방지하기 위해서, 만일 두 마을이 인접해 있으면 두 마을을 모두 '우수 마을'로 선정할 수는 없다. 즉 '우수 마을'끼리는 서로 인접해 있을 수 없다.
선정되지 못한 마을에 경각심을 불러일으키기 위해서, '우수 마을'로 선정되지 못한 마을은 적어도 하나의 '우수 마을'과는 인접해 있어야 한다.
각 마을 주민 수와 마을 사이의 길에 대한 정보가 주어졌을 때, 주어진 조건을 만족하도록 '우수 마을'을 선정하는 프로그램을 작성하시오.


입력

첫째 줄에 정수 N이 주어진다. (1 ≤ N ≤ 10,000) 둘째 줄에는 마을 주민 수를 나타내는 N개의 자연수가 빈칸을 사이에 두고 주어진다. 1번 마을부터 N번 마을까지 순서대로 주어지며, 주민 수는 10,000 이하이다. 셋째 줄부터 N-1개 줄에 걸쳐서 인접한 두 마을의 번호가 빈칸을 사이에 두고 주어진다.

❗ 풀이

대충 봤을 때 DP 문제 같아서 DP로 풀려다가 탐색 순서가 복잡해서 풀기가 귀찮을 것 같아서 탐색하면서 연산하자! 라는 생각으로 재귀로 풀었다. 근데 또 풀리네?

기본 아이디어는 각 노드 별로 해당 노드를 선택했을 때의 최대값선택하지 않았을 때의 최대값을 기준으로 갱신하는 것이다. 각 최대값을 구하기 위해서는 해당 노드와 연결된 노드들의 최대값들을 알아야한다. 주변 노드의 선택 여부에 따라 최대값이 바뀔 수 있기 때문이다.

재귀로 탐색하며

예시

image

위의 예시로 보자.
노드 안의 숫자는 노드 번호, 옆의 숫자는 노드를 선택했을 때 얻을 수 있는 값이 된다.
초록색은 탐색중인 노드, 노란색은 탐색을 시작했지만 종료되지 않은 노드, 빨간색은 탐색 종료된 노드다.

image

1번 부터 탐색한다. 방문 체크를 하고, 연결된 노드들 중에 방문하지 않은 노드들을 우선 방문한다. 순서대로 2번, 3번 노드를 탐색한다. 1, 2번 노드는 탐색이 종료되지 않은 상태다.

재귀 순서대로 탐색하면 4번 노드를 탐색하게 된다.

image

4번 노드는 더 이상 탐색할 노드가 없기 때문에 4번 노드를 선택했을 때의 최대값과 4번 노드를 선택하지 않았을 때의 최대값을 반환한다.

추가로 탐색을 이어가자.

image

6번과 7번 노드도 마찬가지로 5번 노드에 선택했을 때의 최대값, 선택하지 않았을 때의 최대값을 더한다.

image

5번 노드를 선택하면 6, 7번 노드를 선택할 수 없고, 5번 노드를 선택하지 않으면 6, 7번 노드를 선택할 수 있다. 주변 노드의 값들을 더해서 반환하자

image

3번 노드를 선택하는 경우는 3, 6, 7번 노드를 선택하는게 베스트 선택이 된다. 4번을 선택하지 않는 최대 값 0과 5번을 선택하지 않는 최대 값 101, 3번의 1을 더해 102의 값을 가질 수 있다.

여기서 3번 노드를 선택하지 않는 경우의 수는 조금 생각해볼 여지가 있다. 3번을 선택한 경우에는 3번 주변 노드들의 선택하지 않은 최대값 을 더했다. 선택한 노드의 주변 노드는 선택할 수 없기 때문이다. 하지만 3번을 선택하지 않더라도 주변 노드를 꼭 선택할 필요는 없다. 3번을 선택하지 않고 5번도 선택하지 않는다는 선택지가 있다. 그렇게 하면 4, 6, 7번을 골라 201의 최대값을 가질 수 있다. 즉 n번 노드를 선택하지 않았을 때의 최대값은 n번 노드 주변 노드들을 선택한 최대값선택하지 않았을 때의 최대값 중 큰 값을 더하면 된다.

image

마찬가지로 2번, 1번까지 연산하자

image

1번 노드를 선택하지 않았을 때의 최대값은 202, 선택했을 때의 최대값은 301이다. 이 값 둘 중 큰 값이 정답이 된다.

소스코드

#include <iostream>
#include <vector>

#define fastio ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

#define NUM 10002

using namespace std;

// x : 이 노드를 선택하지 않았을 때의 최대값
// y : 이 노드를 선택했을 때의 최대값
struct node {
    int x, y;
    node(int X, int Y) : x(X), y(Y) {};
    int m() { return max(x, y); }
};

bool visited[NUM] = {0, };
int arr[NUM] = {0, };
vector<int> v[NUM];

node f(int t) {
    visited[t] = true;

    node tmp(0, arr[t]);

    // 이 노드에서 갈 수 있는 가지들을 탐색
    for (auto i : v[t]) {
        // 방문한 노드면 가지 않는다
        if (!visited[i]) {
            node a = f(i);

            tmp.x += a.m();
            tmp.y += a.x;
        }
    }

    return tmp;
}

int main() {
    fastio

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

    for (int i = 0; i < n - 1; i++) {
        int a, b; cin >> a >> b;
        v[a].push_back(b);
        v[b].push_back(a);
    }

    printf("%d\n", f(1).m());
}

📚 새롭게 알게된 내용

다 풀고 나니까 풀이는 재귀로 하고 값은 전역 배열로 관리했으면 그냥 DP 문제였지 않았을까?

Copy link
Collaborator

@InSange InSange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음 문제를 접했을때 바로 dp를 파악했지만 dfs접근까지 가기는 어렵더군요
자신을 더하는 단계와 더하지 않는 단계를 구별하여 재귀로 돌아가서 부모 노드에서 다시 계산하는 방법이 인상적이었습니다. 골드 2치고는 난이도가 낮지만 트리와 dp라는 조건만 보았을 때 다른 길로 샐 가능성이 높은 문제같네요..

Copy link
Collaborator

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재가 우수마을인 경우엔 바로 더해주면 되지만 아닌 경우에는 인접 마을이 우수마을인 경우와 아닌 경우 중에서 더 큰 값을 선택하는 거군요. 최댓값을 선택하는 거면 "우수마을로 선정되지 못한 마을은 적어도 하나의 우수 마을과는 인접해야 한다" 를 만족하게 되네요.. 그림 덕분에 잘 이해되었습니다. pr 잘 봤습니다!

@yuyu0830 yuyu0830 merged commit afe59d1 into main Jul 25, 2024
@yuyu0830 yuyu0830 deleted the 19-yuyu0830 branch July 25, 2024 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants