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 ? ‘\n‘ : ‘ ‘);
159     }
160
161     return 0;
162 }
时间: 2024-10-14 00:49:10

SOJ 1140. 国王的遗产的相关文章

编程题目分类(剪辑)

1. 编程入门 2. 数据结构 3. 字符串 4. 排序 5. 图遍历 6. 图算法 7. 搜索:剪枝,启发式搜索 8. 动态规划/递推 9. 分治/递归 10. 贪心 11. 模拟 12. 算术与代数 13. 组合问题 14. 数论 15. 网格,几何,计算几何 [编程入门] PC 110101, uva 100, The 3n+1 problem, 难度 1 PC 110102, uva 10189, Minesweeper, 难度 1 PC 110103, uva 10137, The T

(转)sicily题目分类

Sicily题目分类 ·         [数据结构/图论] 1310 Right-Heavy Tree   笛卡尔树相关,复杂度O(N)或O(NlogN). ·1426 Phone List         电话号码前缀检索,trie树相关. ·1443 Printer Queue      基本队列操作. ·1149 等价表达式         判断表达式是否等价(递归求解) ·1136 山海经             n长序列里求m次区间询问的最大连续子区间和.线段树/RMQ ·1252

2014年 蓝桥杯决赛(Java)

国王的遗产 X国是个小国.国王K有6个儿子.在临终前,K国王立下遗嘱:国王的一批牛作为遗产要分给他的6个儿子. 其中,大儿子分1/4,二儿子1/5,三儿子1/6,.... 直到小儿子分1/9. 牛是活的,不能把一头牛切开分. 最后还剩下11头牛,分给管家. 请计算国王这批遗产中一共有多少头牛. 这是一个整数,请通过浏览器提交答案,不要填写任何多余的内容(比如说明性的文字) 2520 public class Main{ public static void main(String[] args)

sicily 1140(搜索)

题目链接:sicily 1140 解题思路:贪心+深搜 相当考编程能力的一道题,一个手贱就卡了好几天,不过的确是一到好题.考察的是对贪心法的运用,还有编程能力--深搜.贪心原则是从最小结点开始搜索(这样最小结点就是根结点),然后对于每一个结点,搜索返回结点数和最小结点,根据题意比较结果,每次贪心搜索之后删除那条边,并标记整个子图,再继续搜索,直到所有的人都分到遗产. 代码:(有可能冗余很多,但都是测试需要) #include <bits/stdc++.h> #define INF 0x3f3f

凯瑟琳皇后和亨利国王的那些事[转]

http://blog.sina.com.cn/s/blog_66ccfca60102ux3h.html 彼得堡大教堂(见下图和解释). 这个近千年的教堂非常宏伟,英国人花了125年的时间才把它建成.5百年前,大教堂开始倾斜,为了防止进一步倾斜,在旁边又建了一个副楼. 英国每个城市都有教堂,而教堂是这个国家最大的文化和历史遗产.上千年不坏,是因为巧夺天工的建筑艺术,也是因为英国人对文物和教堂的保护意识非常强.许多皇家成员.贵族和社会名流死后,都埋葬在教堂的地下,并刻有文字记载.这些过去的人物,给

计蒜客NOIP模拟赛(3)D1T3 任性的国王

X 国的地图可以被看作一个两行 nn 列的网格状图.现在 X 国需要修建铁路,然而该国的国王非常小气,他只想保证位于某两列之间的所有城市互相可以到达就行了,在此基础上,他希望所花费的代价最小. 铁路可以建在任何两个相邻的点之间,使他们可以互相到达.可以作为工作人员,你已经整理出了为每一对相邻城市架设铁路所需要的花费.你需要准备好回答国王如下形式的问题. 对于 (i,j)(i,j):当前情况下,使第 ii 列到第 jj 列之间的所有城市连通的最小代价是多少(列下标从 11 开始)?注意不能用其他列

[NOIP2012提高组]国王旅行

题目:洛谷P1080.Vijos P1779. 题目大意:国王和每个大臣左.右手各写了一个数.规定每个大臣得到的金币数为他前面所有人左手的数字的乘积除以他自己右手的数(向下取整),现在国王要改变大臣的排列顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少(国王永远站在最前面). 解题思路:(贪心)首先,交换相邻两个大臣只会对这两个大臣造成影响,并不会对其他大臣造成影响. 我们考虑大臣i和i+1,设他们左手数字分别为A[i]和A[i+1],右手分别为B[i]和B[i+1],前i-1个大臣和国王左手的

国王游戏

题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍的最前面.排好队后,所有的大臣都会获得国王奖赏的若干金币,每 位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右 手上的数,然后向下取整得到的结果. 国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序, 使得获得奖赏最多的大臣,所获奖赏尽可能的少

1198 国王游戏

1198 国王游戏 2012年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n位大臣排成一排,国王站在队伍的最前面.排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自