zoukankan      html  css  js  c++  java
  • SOJ 1140. 国王的遗产

    题目大意:给定一棵n个顶点,n-1条边的树。有k个孩子,前k-1个孩子切走树中顶点数不大于n/2的最大连通块,剩余的结点组成新的树。最后一个孩子得到剩余的树中的所有结点。

    按顺序输出每个孩子能获得的顶点数。

    解题思路:任选一结点作为根节点,使用深度优先搜索,获得相对于父节点一端,子节点一端的节点总数和节点最小值。再次使用深度优先搜索,反向求得相对于子节点一端,父节点一端的节点总数和节点最小值。如此能快速获取任意边两端的节点总数和节点最小值。只需要遍历边即可, 可以优化。

    注意:需要证明,任意两种切法中,任意顶点不可能存在于两个合法长度相等的序列中。

    代码如下:

      1 #include <cstdio>
      2 #include <cstdlib>
      3 #include <ctime>
      4 #include <climits>
      5 #include <vector>
      6 #include <map>
      7 using namespace std;
      8 
      9 typedef pair<int, int> P;
     10 
     11 struct Info {
     12     int nodeMinimal;
     13     int nodeCount;
     14 };
     15 
     16 const int maxn = 30001;
     17 
     18 map<int, Info> G[maxn];
     19 
     20 P search(int cur, int upper) {
     21     map<int, Info> &near = G[cur];
     22 
     23     int minimal = INT_MAX;
     24     int count = 0;
     25     map<int, Info>::iterator iter = near.begin();
     26     while (iter != near.end()) {
     27         if (iter->first != upper) {    // 如果不是上一层的结点,就往下搜索
     28             pair<int, int> p = search(iter->first, cur);    // p.first表示下一层的总结点数,p.second表示下一层的最小值
     29             iter->second.nodeCount = p.first;
     30             iter->second.nodeMinimal = p.second;
     31             count += p.first;
     32             minimal = minimal < p.second ? minimal : p.second;
     33         }
     34         ++iter;
     35     }
     36 
     37     return P(count + 1, minimal < cur ? minimal : cur);
     38 
     39 }
     40 
     41 void reverse_search(int cur, int upper, int upper_count, int upper_minimal, int tot) {
     42     // 考虑第一个起点和最底层点
     43     if (upper != -1) {
     44         Info & info = G[cur][upper];    // 反向标记
     45         info.nodeCount = upper_count;
     46         info.nodeMinimal = upper_minimal;
     47     }
     48     // 邻结点只有一个或者多个
     49     map<int, Info> &near = G[cur];
     50     if (near.size() == 1 && upper != -1) return;    // 如果邻结点只有upper,则不需要往下递归
     51     // 如果邻结点有两个或两个以上
     52     // 找最小值和次小值
     53     int most_minimal = INT_MAX, minimal = INT_MAX;
     54     map<int, Info>::iterator iter = near.begin();
     55     while (iter != near.end()) {
     56         int nodeMinimal = iter->second.nodeMinimal;
     57         if (nodeMinimal < most_minimal) {
     58             minimal = most_minimal;
     59             most_minimal = nodeMinimal;
     60         } else if (nodeMinimal < minimal) {
     61             minimal = nodeMinimal;
     62         }
     63         ++iter;
     64     }
     65     if (cur < most_minimal) {
     66         minimal = most_minimal;
     67         most_minimal = cur;
     68     } else if (cur < minimal) {
     69         minimal = cur;
     70     }
     71 
     72 
     73     // 遍历邻结点( != upper),如果
     74     iter = near.begin();
     75     while (iter != near.end()) {
     76         if (iter->first != upper) {    // 如果邻结点不是Upper结点,则可以继续递归搜索
     77             int cur_count = tot - iter->second.nodeCount;
     78             int cur_minimal = iter->second.nodeMinimal == most_minimal ? minimal : most_minimal;
     79             reverse_search(iter->first, cur, cur_count, cur_minimal, tot);
     80         }
     81         ++iter;
     82     }
     83 
     84 }
     85 
     86 void findCutWay(const int & cur, const int & upper, int & from, int & to, int &nodeCount, int &nodeMinimal, const int & tot) {
     87     int half_tot = tot / 2;    // 总数的一半
     88     map<int, Info> & near = G[cur];
     89     map<int, Info>::iterator iter = near.begin();
     90     while (iter != near.end()) {
     91         if (iter->second.nodeCount <= half_tot) {    // 如果相邻块数量符合要求,则操作
     92             if (iter->second.nodeCount > nodeCount) {
     93                 from = cur, to = iter->first;
     94                 nodeCount = iter->second.nodeCount;
     95                 nodeMinimal = iter->second.nodeMinimal;
     96             } else if (iter->second.nodeCount == nodeCount){
     97                 // 假设不出现nodeMinimal == iter->second.nodeMinimal的情况
     98                 nodeMinimal = nodeMinimal < iter->second.nodeMinimal ? nodeMinimal : (from = cur, to = iter->first,iter->second.nodeMinimal);
     99             }
    100             if (iter->second.nodeCount == half_tot && iter->first != upper) // 如果刚好一半,要测试另一侧,可简化;如果少于一半,则不继续递归
    101                 findCutWay(iter->first, cur, from, to, nodeCount, nodeMinimal, tot);
    102         } else if (iter->first != upper) {
    103             findCutWay(iter->first, cur, from, to, nodeCount, nodeMinimal, tot);
    104         }
    105         ++iter;
    106     }
    107 }
    108 
    109 void cut(int & root, int &num, int tot) {
    110     // 从root开始处理整棵树并做标记 G[from][to].nodeCount , G[from][to].nodeMinimal
    111     search(root, -1);
    112 
    113     // 把剩余的标记 : 反向标记
    114     reverse_search(root, -1, 0, INT_MAX, tot);
    115 
    116     // 遍历整棵树,找到可以切的位置
    117     int from, to, nodeCount = 0, nodeMinimal = INT_MAX;
    118     findCutWay(root, -1, from, to, nodeCount, nodeMinimal, tot);
    119 
    120     // 真正切
    121     G[from].erase(to);
    122     G[to].erase(from);
    123     root = from;
    124     num = nodeCount;
    125 }
    126 
    127 int main() {
    128     int n, k;
    129     scanf("%d%d", &n, &k);
    130     int a, b;
    131     for (int i = 1; i < n; ++i) {
    132         scanf("%d%d", &a, &b);
    133         G[a][b];
    134         G[b][a];
    135     }
    136     // srand(rand());
    137     int root = rand() % n + 1;    // 随机生成根节点
    138 
    139     int num;
    140     int sum = 0;
    141     vector<int> ans;
    142     while (--k, k) {    // 对金块链 切k-1次
    143         cut(root, num, n - sum);
    144         sum += num;
    145         ans.push_back(num);
    146         // 如果剩余的少于2,则给下一位,并且退出
    147         if (n - sum < 2) {
    148             break;
    149         }
    150     }
    151 
    152     // ans.push_back() 剩下的连通图  n - 前面的总和
    153     ans.push_back(n - sum);
    154     for (int i = 1; i < k; ++i) ans.push_back(0);
    155 
    156     // 输出结果
    157     for (int i = 0; i < ans.size(); ++i) {
    158         printf("%d%c", ans[i], i == ans.size() - 1 ? '
    ' : ' ');
    159     }
    160 
    161     return 0;
    162 }
  • 相关阅读:
    mac 10.15.7 修改PATH
    oc 属性类型一般用法
    ubuntu解压zip文件名乱码
    telnet 退出
    docker 根据容器创建镜像
    mac android adb device 没有显示设备
    Yii2 查看所有的别名 alias
    Yii2 App Advanced 添加 .gitignore
    ubuntu 18.04 搜狗突然就提示乱码
    An error occured while deploying the file. This probably means that the app contains ARM native code and your Genymotion device cannot run ARM instructions. You should either build your native code to
  • 原文地址:https://www.cnblogs.com/mchcylh/p/4944850.html
Copyright © 2011-2022 走看看