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 }
  • 相关阅读:
    linux分区
    MySQL
    RGB中的颜色的设置
    解决UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 12: ordinal not in range(128)的编码问题
    解决运行scrapy是报错No module named cryptography,解决cryptography的安装问题,解决libffi的安装问题
    解决pycharm下安装reportLab报错的问题
    简单的爬取并下载图片的程序
    linux常用命令
    ubuntu下安装pycharm的方法
    win7下装ubuntu双系统后无法进入win7的解决方法
  • 原文地址:https://www.cnblogs.com/mchcylh/p/4944850.html
Copyright © 2011-2022 走看看