[编程题]: 小团的配送团队(2021美团校招题)

小团的配送团队

小团是美团外卖的区域配送负责人,众所周知,外卖小哥一般都会同时配送若干单,小团在接单时希望把同一个小区的单子放在一起,然后由一名骑手同意配送。但是由于订单是叠在一起的,所以,他归类订单时只能知道新订单和已有的某个订单的小区是相同的,他觉得这样太麻烦了,所以希望你帮他写一个程序解决这个问题。

即给出若干个形如a b的关系,表示a号订单和b号订单是同一个小区的,请你把同一个小区的订单按照编号顺序排序,并分行输出,优先输出最小的订单编号较小的小区订单几何。订单的编号是1到n。(可能存在同时出现a b和b a这样的关系,也可能出现a a这样的关系)

样例输入
5 5
1 2
2 2
3 1
4 2
5 4
样例输出
1
1 2 3 4 5

样例输入2(自己构造的)
8 6
5 3
6 8
4 6
1 2
2 3
5 7
样例输出
2
1 2 3 5 7
4 6 8

解题思路

很容易看出来,这是跟几何密切相关的题,更几何相关的数据结构最常用的就是——并查集了。
同一个小区的单子在同一个集合中

代码如下:
Java

import java.util.*;

public class Main{
    private static int[] father;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int m = input.nextInt();
        father = new int[n + 1];
        for (int i = 0; i < n + 1; i++) {
            father[i] = i;
        }
        int city1 , city2;
        for (int i = 0; i < m; i++) {
            city1 = input.nextInt();
            city2 = input.nextInt();
            union(city1, city2);
        }
        Map<Integer, TreeSet<Integer>> res = new TreeMap<>();
        for (int i = 1; i <= n; i++) {
            int fa = findFather(i);
            TreeSet<Integer> tempSet = res.getOrDefault(fa, new TreeSet<>());
            tempSet.add(i);
            res.put(fa, tempSet);
        }
        System.out.println(res.size());
        Set<Integer> keySet = res.keySet();
        for (int key: keySet) {
            Set<Integer> tempSet = res.get(key);
            int size = 0;
            for (int id: tempSet) {
                System.out.print(id);
                size++;
                if (size < tempSet.size()) System.out.print(" ");
                else System.out.println();
            }
        }
    }

    public static int findFather(int a) {
        int x = a;
        while (x != father[x]) x = father[x];
        while (a != father[a]) {
            int z = a;
            a = father[a];
            father[z] = x;
        }
        return x;
    }

    public static void union(int a, int b) {
        int fa = findFather(a);
        int fb = findFather(b);
        if (fa != fb) {
            //father[fa] = fb;  //若要保证含有最小单号的小区先输出,那么久不能随便合并集合了,要将小的那个根作为整个几何的根
            if (fa < fb) {
                father[fb] = fa;
            } else {
                father[fa] = fb;
            }
        }
    }
}

C++

#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>

using namespace std;

vector<int> father;

int findFather(int a) {
    int x = a;
    while (x != father[x]) x = father[x];
    while (a != father[a]) {
        int z = a;
        a = father[a];
        father[z] = x;
    }
    return x;
}

void unionCollection(int a, int b) {
    int fa = findFather(a);
    int fb = findFather(b);
    if (fa < fb) {
        father[fb] = father[fa];
    } else {
        father[fa] = father[fb];
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i <= n; i++) father.push_back(i);
    int a1, a2;
    for (int i = 0; i < m; i++) {
        cin >> a1 >> a2;
        unionCollection(a1, a2);
    }
    map<int, set<int>> res;
    for (int i = 1; i <= n; i++) {
        int fa = findFather(i);
        res[fa].insert(i);
    }
    cout << res.size() << endl;
    for (auto& each: res) {
        int size = 0;
        for (int num: each.second) {
            cout << num;
            size++;
            if (size < each.second.size()) cout << " ";
            else cout << endl;
        }
    }
    return 0;
}

另一个样例

样例输入3(自己构造的)
22 13
5 3
5 7
7 15
6 8
9 10
11 10
9 9
4 6
2 3
1 3
20 21
22 20
21 22

//father[fa] = fb;  //若要保证含有最小单号的小区先输出,那么久不能随便合并集合了,要将小的那个根作为整个集合的根
不这样的话输出如下:
11
4 6 8
9 10 11
12
13
14
1 2 3 5 7 15
16
17
18
19
20 21 22
很明显不符合要求
改变集合合并的方式后,输出如下:
11
1 2 3 5 7 15
4 6 8
9 10 11
12
13
14
16
17
18
19
20 21 22
这样才符合先输出包含最小单号的小区的要求

猜你喜欢

转载自blog.csdn.net/qq_27198345/article/details/108033661