树状结构学习(1)、最大-最小划分树
来源:互联网 发布:淘宝的充值平台在哪里 编辑:程序博客网 时间:2024/05/21 01:53
树状结构学习(1)、最大-最小划分树
有这样一类题:
给一棵n个点的树,要求将它分成k个子树,使权值和最小的子树权值和尽量大。
解法一:二分答案
我们知道,如果已知权值下限,我们可以从下往上搜以O(n)的复杂度判断是否可以分成k个权值大于权值下限的子树。
缺点:没有真正解出这道题,如果权值是实数或很大将会使这种方法失效。
#include <iostream>#include <cmath>#include <cstring>#include <cstdio>#include <algorithm>#include <vector>using namespace std;const int MAXN = 1e5 + 10;int n, k;int size[MAXN];int bri[MAXN];int fa[MAXN];int w[MAXN];vector<int> map[MAXN];void dfs(int x, int father) { size[x] = w[x]; for (int i = 0; i < map[x].size(); ++i) { int y = map[x][i]; if (y == father) continue; fa[y] = x; dfs(y, x); size[x] += size[y]; }}int tot;void dfs(int x, int father, int val) { int delta = w[x]; for (int i = 0; i < map[x].size(); ++i) { int y = map[x][i]; if (y == father) continue; dfs(y, x, val); delta += size[y]; } size[x] = delta; if (size[x] >= val) { ++tot; size[x] = 0; }}bool check(int x) { tot = 0; for (int i = 1; i <= n; ++i) bri[i] = size[i]; dfs(1, -1, x); for (int i = 1; i <= n; ++i) size[i] = bri[i]; if (tot >= k) return true; else return false;}int main(){ freopen("in.txt", "r", stdin); freopen("out1.txt", "w", stdout); int a, b; scanf("%d%d", &n, &k); for (int i = 1; i < n; ++i) { scanf("%d%d", &a, &b); map[a].push_back(b); map[b].push_back(a); } int l = 1, r = 0; for (int i = 1; i <= n; ++i) scanf("%d", &w[i]), r += w[i]; r /= k; dfs(1, -1); while (l < r) { int mid = (l + r + 1) >> 1; if (check(mid)) `l = mid; else r = mid - 1; } printf("%d", l); return 0;}
解法二:
移动割法
算法流程:
(1)选择入度为1的结点作为起始,将k-
1个割都放在与根相连的唯一的边上。
(2)计算当前划分的最小值Wmin
(3)移动割,并求出所有移动方法最大的子树权值和Wmax
(4)如果Wmax > Wmin,那么移动,并跳到(2)
(5)结束。Wmin为所求。当前割的位置即为一个可行的最优方案。
算法证明之后传。
#include <iostream>#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int MAXN = 100000 + 10;const int INF = 0x7fffffff;int n, k, Wmin, start; int in[MAXN]; //入度 int size[MAXN];int fa[MAXN]; //fatherint vec[MAXN]; //每一个割现在的容量int have[MAXN]; //结点x是否有一个割 int pos[MAXN]; //割的位置 int w[MAXN]; //权重 int totSize = 0;vector<int> map[MAXN];void dfs(int x, int father) { size[x] = w[x]; totSize += size[x]; for (int i = 0; i < map[x].size(); ++i) { int y = map[x][i]; if (y == father) continue; fa[y] = x; dfs(y, x); size[x] += size[y]; }}bool down() { int Wmax = 0, next = 0, id = 0; for (int i = 1; i <= k; ++i) { int x = pos[i]; for (int j = 0; j < map[x].size(); ++j) { int y = map[x][j]; if (y == fa[x] || have[y]) continue; if (size[y] >= Wmin && size[y] > Wmax) { Wmax = size[y]; next = y; id = i; } } } if (!next) return false; --have[pos[id]]; int tmp = size[pos[id]] - size[next], i = fa[pos[id]], flag = 1; while (i && flag) { if (have[i]) flag = 0; size[i] += tmp; i = fa[i]; } size[pos[id]] -= size[next]; pos[id] = next; ++have[next]; return true;} int query() {/* cout<<"Status of have[]"<<endl; for (int i = 1; i <= n; ++i) printf("(%d, %d), ", i, have[i]); cout<<endl; cout<<"Status of size[]"<<endl; for (int i = 1; i <= k; ++i) cout<<size[pos[i]]<<" ";*/ int res = INF, sum = 0; for (int i = 1; i <= k; ++i) if (pos[i] != pos[i-1]) { res = min(res, size[pos[i]]); sum += size[pos[i]]; }// cout<<totSize - sum<<endl; res = min(res, totSize - sum); return res;}int move(int start) { Wmin = w[start]; while (true) { if (!down()) break; Wmin = query(); } return Wmin; }int main(){ freopen("in.txt", "r", stdin); freopen("out2.txt", "w", stdout); int a, b, Wstart = INF; scanf("%d%d", &n, &k); --k; for (int i = 1; i < n; ++i) { scanf("%d%d", &a, &b); map[a].push_back(b); map[b].push_back(a); ++in[a]; ++in[b]; } for (int i = 1; i <= n; ++i) { scanf("%d", &w[i]); if ((in[i] == 0 || in[i] == 1) && w[i] < Wstart) start = i, Wstart = w[i]; } dfs(start, -1); if (k == 0) printf("%d", totSize); else { for (int i = 1; i <= k; ++i) pos[i] = map[start][0]; have[map[start][0]] = k; printf("%d", move(start)); } return 0;}
附:随机数生成程序
#include <iostream>#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <ctime>using namespace std;int main(){ srand(time(0)); freopen("in.txt", "w", stdout); int n = rand()%10000 + 1, k = rand()%n / 2 + 1; cout<<n<<" "<<k<<endl; for (int i = 2; i <= n; ++i) cout<<rand()%(i-1)+1<<" "<<i<<endl;; for (int i = 1; i <= n; ++i) cout<<rand()%100000 + 1<<endl; return 0;}
阅读全文
0 0
- 树状结构学习(1)、最大-最小划分树
- hdu 4417 Super Mario(划分树或树状数组)
- 区间DP 最大面积最小的三角形划分
- <考试题> bzoj 1821 部落划分 (二分、最小生成树)
- bzoj1821: [JSOI2010]Group 部落划分 Group(最小生成树)
- 树状结构(经典)
- (13)树状结构
- 二叉树深度(最大和最小)
- 线段树模板(求最大最小)
- 【漫谈机器学习】1.误差最小VS概率最大(1)
- HDU 4417 Super Mario (树状数组+离线处理)(划分树+二分答案)
- 树状结构之主席树
- HEVC代码学习10:实现全局使用最小CU划分
- 线段树+树状数组+二分+划分树+主席树+hdu4417
- hdu4417(树状数组)(线段树)(划分树+二分)
- HDU 4417 Super Mario 划分树/树状数组
- hdu 4417 Super Mario(离线树状数组|划分树)
- 【日常学习】【划分DP】codevs1017 乘积最大题解
- 如何在Windows下装Unbuntu
- [NOIP模拟] 区间
- 版本控制之Git---脚本提交
- ubuntu16.04安装sbt
- RSA算法原理(二)
- 树状结构学习(1)、最大-最小划分树
- 【C++】通过模板实现一个通用的冒泡排序
- go get 使用代理
- spring boot,cloud,微信点餐系统,小程序开发视频教程
- iOS小技巧-为项目添加全局PrefixHeader.pch预编译文件
- 有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
- [codeforces700B]-Connecting Universities-贪心策略
- 【笔记】深度学习框架简介
- 【BZOJ1968】约数研究(数论)