1. 程式人生 > 其它 >L2-2 小字輩(搜尋/並查集)

L2-2 小字輩(搜尋/並查集)

題意

本題給定一個龐大家族的家譜,要請你給出最小一輩的名單。

輸入格式

輸入在第一行給出家族人口總數 N(不超過 100 000 的正整數) —— 簡單起見,我們把家族成員從 1 到 N 編號。隨後第二行給出 N 個編號,其中第 i 個編號對應第 i 位成員的父/母。家譜中輩分最高的老祖宗對應的父/母編號為 -1。一行中的數字間以空格分隔。

輸出格式

首先輸出最小的輩分(老祖宗的輩分為 1,以下逐級遞增)。然後在第二行按遞增順序輸出輩分最小的成員的編號。編號間以一個空格分隔,行首尾不得有多餘空格。

樣例1

input
9
2 6 5 5 -1 5 6 4 7
output
4
1 9

思路

這道題就是開vector二維陣列存父子關係,然後就搜唄。感覺深搜比廣搜直接。

具體做法都是先把每個爹的兒子們記錄下來,其中是-1的那個是老祖宗,我們從老祖宗開始往下搜。用maxd更新最大輩分。

第3種思路:用並查集存爹,然後邊打標記邊列舉每個兒子,分別遞迴找爹。

思路1(DFS)

#include <bits/stdc++.h>
using namespace std;
const int N = 5 + 1e5;

vector<int> son[N]; // 下標是爹,值是兒子
int ans[N], root, maxd = 1, idx = 0; // root:老祖宗 maxd:最深深度

void dfs(int x, int d) {
    int n = son[x].size();
    if (n == 0) { // 當前這個兒子沒有兒子了
        if (maxd < d) { // 是否需要更新深度
            maxd = d;
            idx = 0; // 深度更新了,前面存的都不算,抹掉
            ans[idx++] = x;
        }
        else if (maxd == d) // 他的輩分等於當前已知的最小輩分,加入ans
            ans[idx++] = x;
    }
    else // 還有兒子
        for (int i = 0; i < n; i++)
            dfs(son[x][i], d + 1);
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int t;
        scanf("%d", &t);
        if (t == -1)
            root = i;
        else
            son[t].push_back(i);
    }
    dfs(root, 1);
    if (maxd == 1) printf("1\n1\n");
    else {
        printf("%d\n%d", maxd, ans[0]);
        for (int i = 1; i < idx; i++)
            printf(" %d", ans[i]);
    }
    return 0;
}

思路2(BFS)

#include <bits/stdc++.h>
using namespace std;
const int N = 5 + 1e5;

vector<int> son[N];
int depth[N];
int n, root;

void bfs(int u) {
    queue<int> q;
    q.push(u);
    while (q.size()) {
        int x = q.front(); q.pop();

        for (int i = 0; i < son[x].size(); i++) {
            if (!depth[son[x][i]]) {
                q.push(son[x][i]);
                depth[son[x][i]] = depth[x] + 1;
            }
        }
    }
    int maxd = -1;
    for (int i = 1; i <= n; i++)
        if (depth[i] > maxd) maxd = depth[i];
    cout << maxd + 1 << '\n';
    for (int i = 1, nf = 0; i <= n; i++)
        if (depth[i] == maxd) {
            if (nf++) cout << ' ';
            cout << i;
        }
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
    	int t;
        scanf("%d", &t);
        if (t == -1)
        	root = i;
        else
        	son[t].push_back(i);
    }
    bfs(root);
    return 0;
}

思路3(並查集)

#include <bits/stdc++.h>
using namespace std;
const int N = 5 + 1e5;

int fa[N]; // 存爹,順便用來求distance
int a[N]; // 存距離

int find(int x) {
    if (a[x]) return a[x]; // 存過了就直接返回,就再存,否則tle

    if (fa[x] == -1) return fa[x] = 1;
    else return a[x] = find(fa[x]) + 1;
}

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

    int maxd = 0;
    for (int i = 1; i <= n; i++) {
        int t;
        t = get(i);
        if (t > maxd) maxd = t;
    }

    cout << maxd << '\n';
    for (int i = 1, nf = 0; i <= n; i++)
        if (a[i] == maxd) {
            if (nf++) cout << ' ';
            cout << i;
        }
}