首页 > 试题广场 >

1-E:疫情

[编程题]1-E:疫情
  • 热度指数:871 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

现有 个城市,这 个城市构成了一棵树,即这 个城市中有 条边,每条边都连接着两个不同的城市,使得从任意一个城市出发,通过若干条边能达到其它任意一个城市,且每个城市都有一个正整数值 代表这个城市的人口密集度。

突然某天瘟疫爆发,瘟疫会在人口密集度大于或等于 的城市中肆意横行,这些城市会进行封城,与这些城市相连的边都会被切断。这时这棵树就会被切分为若干连通块,同一个连通块中的城市之间可以通过若干条边互相达到。

政府为了稳定局势,想请你求出在瘟疫爆发后城市形成的连通块的数量小于等于 的情况下, 的最小值可以是多少,当的值可以无穷小时,输出


输入描述:

第一行二个正整数

第二行 个正整数 ,分别代表 个城市的人口密集度。

接下来 行,每行二个正整数 ,代表城市 与城市 之间有一条边,数据保证 个城市构成一棵树。



输出描述:

一个整数代表  的最小值。

示例1

输入

5 2
1 2 3 4 5
1 2
2 5
5 3
4 1

输出

6

说明

时,没有任何城市的密集度大于或等于,没有城市被封城,所有的城市能互相达到并构成个连通块,数量小于,此时满足条件且最小。
示例2

输入

5 3
1 2 3 4 5
1 2
1 4
2 3
2 5

输出

4

说明

时,城市和城市会被封城:城市和城市的边被切断,城市与城市的边被切断。
此时形成了个连通块:
城市可以相互达到构成了一个连通块。
城市单独构成了一个连通块。
城市单独构成了一个连通块。
此时满足条件且最小。
这个题好难,感谢评论区大佬给的思路,附上一个java版本
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        TreeSet<Integer> densitySet = new TreeSet<>((a, b) -> b - a);
        int[] density = new int[n];
        for(int i = 0; i < n; i++){
            density[i] = sc.nextInt();
            densitySet.add(density[i]);
        }
        int[] degree = new int[n];
        for(int i = 0; i < n - 1; i++){
            int x = sc.nextInt() - 1, y = sc.nextInt() - 1;
            if(density[x] > density[y]){
                degree[x]++;
            }else{
                degree[y]++;
            }
        }
        HashMap<Integer, Integer> map = new HashMap<>();      // 存储同一人口密度下,一共有多少度
        for(int i = 0; i < n; i++){
            map.put(density[i], map.getOrDefault(density[i], 0) + degree[i]);
        }
        int res = 0;
        Iterator<Integer> iter = densitySet.iterator();
        m--;
        int index = 0;
        while(m >= 0 && iter.hasNext()){
            res = iter.next();
            m -= map.get(res);
        }
        System.out.println(iter.hasNext()? res + 1: -1);
    }
}
顺带提一句,数据输入格式有点迷,用缓冲流读取数据会出问题,非得用扫描器。
发表于 2022-01-20 16:11:54 回复(0)
class Solution:
    def minValueofh(self, n, m, line, array):
        from collections import defaultdict
        dic = defaultdict(list)
        for a, b in array:
            dic[a].append(b)
            dic[b].append(a)
        for i in range(m, 0, -1):
            if not self.check(i, line, dic):
                continue
            else:
                return self.check(i, line, dic)
        return -1
    def check(self, i, line, dic):
        leaf = []
        father = {}
        k = 1
        for f, s in dic.items():
            if len(s) == 1:
                leaf.append(f)
            else:
                father[f] = len(s)
                flag = max(line) + 1
            while flag > 0:
                for f, s in dic.items():
                    if line[f-1] >= flag:
                        if f in leaf:
                            k += 1
                        else:
                            k += father[f]
                    else:
                        continue
                if i == k:
                    return flag
                flag -= 1
            return 0
if __name__ == "__main__":
    [n, m] = list(map(int, input().split()))
    line = list(map(int, input().split()))
    array = []
    for _ in range(n-1):
        lists = list(map(int, input().split()))
        array.append(lists)
    s = Solution()
    ans = s.minValueofh(n, m, line, array)
    print(ans)

发表于 2021-09-15 17:05:36 回复(0)
Cpp
#include<bits/stdc++.h>
using namespace std;
 
int main() {
    int n, m; cin >> n >> m;
    if(n < m) return -1;
    int a[n]; set<int> us; for(int i = 0; i < n; ++i) cin >> a[i], us.insert(a[i]); //过滤重复值,其使其有序
    int d[n]; memset(d, 0, sizeof(d)); for(int i = 0, x, y; i < n-1; ++i) cin >> x >> y, a[x-1] > a[y-1] ? d[x-1]++ : d[y-1]++;
    unordered_map<int, int> nums; for(int i = 0; i < n; ++i) nums[a[i]] += d[i]; //同样密度的点的度
    auto iter = us.rbegin(); m--;
    while(m >= 0 && iter != us.rend()) {
        m -= nums[*iter]; //连通块数量和点的度相关
        ++iter;
    }
    cout << (iter == us.rend() ? -1 : *prev(iter)+1);
    return 0;
}


发表于 2021-09-17 16:55:24 回复(3)
注意到 树的连通分支数量=点-边

先排序然后从大城市开始删除边,维护总边数即可。记录每个城市的总边数,删除即为当前总边数-删除城市的边数。
为了防止边被多次删除,删除某个城市后周围城市的边数-1。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Da {
    int val, pos;
    Da() :val(0) {};
    Da(int v) :val(v) {};
    bool operator<(Da da1) {
        return (val > da1.val);
    }
};
const int maxnn = 100000;
int main() {
    
    int n, m, i,hn;
    int x, y;
    Da da[maxnn];
    vector<int> edg[maxnn];
    int nedg[maxnn] = { 0 };
    int tnedg;
    cin >> n >> m;
    for (i = 0; i < n; ++i) {
        cin >> x;
        da[i].val = x;
        da[i].pos = i;
    }
    sort(da, da + n);
 //   for (i = 0; i < n; ++i) {
 //       cout << da[i].val << ' ' << da[i].pos<<endl;
  //  }
    for (i = 0; i < n - 1; ++i) {
        cin >> x >> y;
        nedg[x-1]++;
        nedg[y-1]++;
        edg[x-1].push_back(y-1);
        edg[y-1].push_back(x-1);
    }
    if (n == m) cout << -1<<endl;
    else {
        hn = 0;
        tnedg = n - 1;
        while (n - tnedg <= m) {
        //    cout << tnedg << ' '<< da[hn].pos<<' '<<nedg[da[hn].pos] << endl;
        //    system("pause");
            tnedg -= nedg[da[hn].pos];
            for (i = 0; i < edg[da[hn].pos].size(); i++) {
                nedg[edg[da[hn].pos][i]]--;
            }
            hn++;
        }
        cout << da[hn-1].val+1<<endl;
    }
}

发表于 2022-07-04 23:45:36 回复(0)
//并查集+二分
//已通过全部用例
//二分查找h的最小值。将所有小于h的城市按照原树中结构相连(使用并查集),并统计当前h下的连通块数量。
#include <bits/stdc++.h>
#include <unordered_set>
using namespace std;
typedef long long ll;

int Find(vector<int>& parent, int index){
    if(parent[index] != index){
        parent[index] = Find(parent, parent[index]);
    }
    return parent[index];
}

void Union(vector<int>& parent, int index1, int index2){
    parent[Find(parent, index1)] = Find(parent, index2);
}

int cal(unordered_set<int> sp, vector<int>& parent, vector<pair<int, int>>& b){
    for(int i = 0; i < b.size(); i++){
        int x = b[i].first, y = b[i].second;
        if(!sp.count(x) && !sp.count(y)){
            Union(parent, x, y);
        }
    }
    unordered_set<int> count;
    for(int i = 1; i < parent.size(); i++){
        count.insert(Find(parent, i));
    }
    return count.size();
}
int main(){
    int n, m;
    cin>>n>>m;
    vector<int> a(n);
    for(int i = 0; i < n; i++) cin>>a[i];
    vector<pair<int, int>> b(n - 1);
    for(int i = 0; i < n - 1; i++) cin>>b[i].first>>b[i].second;
    vector<int> parent(n + 1, 0);
    for(int i = 1; i <= n; i++) parent[i] = i;
    if(n == m) cout<<-1;
    else{
        int l = 0, r = *max_element(a.begin(), a.end()) + 1;
        while(l < r){
            int mid = (l + r) >> 1;
          
            unordered_set<int> sp;
            for(int i = 0; i < n; i++){
                if(a[i] >= mid) sp.insert(i + 1);
            }
            vector<int> p = parent;
            int now = cal(sp, p, b);
          //  cout<<l<<' '<<r<<' '<<now<<endl;
            if(now <= m) r = mid;
            else l = mid + 1;
        }
        cout<<r;
    }
}

发表于 2022-05-09 10:52:23 回复(0)
#解法:并查集,先按题目给的城市关系集合进行连接,
#根据城市人口密度从大到小进行排序然后从开始剔除密度最高的城市进而更新城市关系集合再次重构并查集,
#直到连通数大于m,记录每次更新的城市中密度最高的然后加1形成一个每个连通数对应的h值列表,
#表中的最小值即是答案
#题目给的测试数据不符合题目给的输入要求所以我只测了示例,
class UnionFind(object):
    def __init__(self, n):
        """长度为n的并查集"""
        self.uf = [-1 for i in range(n + 1)]  # 列表0位置空出
        self.sets_count = n  # 判断并查集里共有几个集合, 初始化默认互相独立

    def find(self, p):
        """尾递归"""
        if self.uf[p] < 0:
            return p
        self.uf[p] = self.find(self.uf[p])
        return self.uf[p]

    def union(self, p, q):
        """连通p,q 让q指向p"""
        proot = self.find(p)
        qroot = self.find(q)
        if proot == qroot:
            return
        elif self.uf[proot] > self.uf[qroot]:  # 负数比较, 左边规模更小
            self.uf[qroot] += self.uf[proot]
            self.uf[proot] = qroot
        else:
            self.uf[proot] += self.uf[qroot]  # 规模相加
            self.uf[qroot] = proot
        self.sets_count -= 1  # 连通后集合总数减一

    def is_connected(self, p, q):
        """判断pq是否已经连通"""
        return self.find(p) == self.find(q)  # 即判断两个结点是否是属于同一个祖先

n, m = map(int, input().split(' '))
if n <= m:
    print(-1)
else:
    r = list(map(int, input().split(' ')))
    rate = []
    for i in range(1, n + 1):
        rate.append((i, r[i - 1]))
    relation = []
    del_rel = []
    uf = UnionFind(n)
    for i in range(n - 1):
        a, b = map(int, input().split(' '))
        relation.append((a, b))
        uf.union(a, b)
    h = []
    while uf.sets_count <= m:
        print(uf.sets_count)
        item = max(rate, key=lambda x: x[1])
        # print("item:{}".format(item))
        rate.remove(item)
        # print("rate:{}".format(rate))
        h.append(item[1] + 1)
        for i in range(len(relation) - 1, -1, -1):
            if relation[i][0] == item[0]:
                relation.remove(relation[i])
            elif relation[i][1] == item[0]:
                relation.remove(relation[i])
        # print("relation:{}".format(relation))
        uf = UnionFind(n)
        for ra in relation:
            uf.union(ra[0], ra[1])
        # print(uf.sets_count)
    print(min(h))

发表于 2021-09-22 01:24:00 回复(0)
8/22对,有很多的测试案例是有问题的。
import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        //BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
        String[] strs = reader.readLine().split(" ");
        int n = Integer.parseInt(strs[0]);
        int m = Integer.parseInt(strs[1]);
        int[] city = new int[n+1];
        int[] arr = new int[n+1];
        strs = reader.readLine().split(" ");
        for (int i = 0; i < n; i++) {
            city[i+1] = Integer.parseInt(strs[i]);
            arr[i+1] = city[i+1];
        }
        boolean[][] tree = new boolean[n+1][n+1];
        String ll = null;
        while((ll = reader.readLine())!=null){ 
            strs = ll.split(" ");
            int a = Integer.parseInt(strs[0]);
            int b = Integer.parseInt(strs[1]);
            tree[a][b] = true;
            tree[b][a] = true;
        }
        Arrays.sort(arr);
        int maxh = 0;
        boolean flag = false;
        for (int i = 1; i <= n; i++) {
            boolean[][] treeCopy = getTreeCopy(tree, n);
            int a = arr[i];
            maxh = i;
            ArrayList<Integer> thecity = new ArrayList<>(n);
            for (int j = 1; j <= n; j++) {
                if (city[j] >= a) {
                    thecity.add(j);
                }
            }
            for (int j : thecity) {
                for (int x = 1; x <= n; x++) {
                    treeCopy[x][j] = false;
                }
                for (int x = 1; x <= n; x++) {
                    treeCopy[j][x] = false;
                }
            }
            int connect = getC(treeCopy, n);
            if (connect <= m) {
                flag = true;
                break;
            }

        }
        if (maxh == 1) {
            System.out.println(-1);
        } else if (!flag) {
            System.out.println(arr[n] + 1);
            //System.out.println(maxh);//;arr[maxh-1] + 1);
        } else {
            System.out.println(arr[maxh - 1] + 1);
        }
    }

    private static boolean[][] getTreeCopy(boolean[][] tree, int n) {
        boolean[][] res = new boolean[n + 1][n + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                res[i][j] = tree[i][j];
            }
        }
        return res;
    }

    private static int getC(boolean[][] tree, int n) {//找连通块
        boolean[] flag = new boolean[n+1];
        int num = 0;
        for (int i = 1; i <= n; i++) {
            if (!flag[i]) {
                num++;
                flag[i] = true;
                dfs(i, tree, n, flag);
            }
        }
        return num;
    }

    private static void dfs(int index, boolean[][] tree, int n, boolean[] flag) {
        boolean has = false;
        for (int i = 1; i <= n; i++) {
            if (i == index || flag[i]) {
                continue;
            } else {
                if (tree[index][i]) {
                    flag[i] = true;
                    has = true;
                    dfs(i, tree, n, flag);
                }
            }
        }
    }

}

发表于 2021-09-11 20:17:20 回复(0)