机试题——找城市

题目描述

一张地图上有 n 个城市,城市和城市之间有且只有一条道路相连:要么直接相连,要么通过其它城市中转相连(可中转一次或多次)。城市与城市之间的道路都不会成环。

当切断通往某个城市 i 的所有道路后,地图上将分为多个连通的城市群,设该城市 i 的聚集度为 DPi(Degree of Polymerization),DPi = max(城市群1的城市个数,城市群2的城市个数,…城市群m 的城市个数)

请找出地图上 DP 值最小的城市(即找到城市 j,使得 DPj = min(DP1, DP2, …, DPn))。

提示

  • 如果有多个城市都满足条件,这些城市都要找出来(可能存在多个解)。
  • DPi 的计算,可以理解为已知一棵树,删除某个节点后,生成的多个子树,求解多个子树节点数的问题。

输入描述

  • 第一行有一个整数 N,表示有 N 个节点。1 <= N <= 1000
  • 接下来的 N-1 行每行有两个整数 xy,表示城市 x 与城市 y 连接。1 <= x, y <= N

输出描述

输出城市的编号。如果有多个,按照编号升序输出。

用例输入

5 
1 2 
2 3 
3 4 
4 5

树的样子如下:
1
|
2
|
3
|
4
|
5
3
6 
1 2 
2 3 
2 4 
3 5 
3 6

树的样子如下:
      1
      |
      2
     / \
    3   4
   / \
  5   6
2 3

将通往2或者3的所有路径切断,最大城市群数量是3,其他任意城市切断后,最大城市群数量都比3大,所以输出2 3

解题思路

对于每个节点 i,我们需要计算删除节点 i 后,剩下的各个子树的最大节点数。删除一个节点相当于把树分成多个子树,这些子树的大小就是我们要计算的值。

  1. 计算每个节点的子树大小:

    • 对树进行深度优先搜索(DFS)遍历,计算每个节点的子树大小。
    • 每个节点的子树大小是通过递归计算它的所有子节点的子树大小得到的。
  2. 删除节点后子树的最大大小:

    • 对于每个节点,删除它后,树会分成多个子树。
    • 删除节点 i 后,子树的最大大小可以通过对它的所有子节点的子树大小求最大值来获得。
  3. 删除节点后对子树大小的计算:

    • 当节点 i 被删除时,父节点将变成新的根节点。因此,父节点的子树大小需要重新计算。
    • 可以在树的遍历中,通过递归计算每个节点的子树大小,并维护一个数组来存储每个节点的最大子树大小。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
using namespace std;
int n;
vector<int> tree[1001];
int vis[1001];// 标记遍历过的节点
int dp[1001];//每个节点的dp值

int dfs(int root,int no) {
    //对于root节点的子树节点数量
    if (vis[root]) return 0;
    else {
        vis[root] = 1;
        int res = 1;
        for (int i = 0; i < tree[root].size(); i++) {
            int next = tree[root][i];
            if (next == no) continue;
            res += dfs(next,no);
        }
        return res;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false); 
    cin.tie(nullptr);  
    cin >> n;
    memset(vis, 0, sizeof(vis));
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < n - 1; i++) {
        int x, y;
        cin >> x >> y;
        tree[x].push_back(y);
        tree[y].push_back(x);
    }
    for (int i = 1; i <= n; i++) {
        // 计算其邻居子树的大小
        int size = tree[i].size();
        vector<int> temp(size); // size个子树
        for (int j = 0; j < size; j++) {
            temp[j] = dfs(tree[i][j],i); //i被断掉
            //cout << j << "  " << temp[j] << endl;
        }
        dp[i] = *max_element(temp.begin(), temp.end());
        //cout <<i<<" "<< dp[i] << endl;
        memset(vis, 0, sizeof(vis));//重置vis
    }
    int res = *min_element(dp+1, dp + n);
    for (int i = 1; i <= n; i++) {
        if (dp[i] == res) cout << i << " ";
    }
    cout << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值