博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
51nod1307(暴力树剖/二分&dfs/并查集)
阅读量:5275 次
发布时间:2019-06-14

本文共 3610 字,大约阅读时间需要 12 分钟。

题目链接:

 

题意: 中文题诶~

 

思路:

解法1:暴力树剖

用一个数组 num[i] 维护编号为 i 的边当前最大能承受的重量. 在加边的过程中根据给出的父亲节点将当前边所在的链上所有边的num都减去当前加的边的重量, 注意当前边也要减自重. 那么当num首次出现负数时加的边号即位答案;

事实上这个算法的时间复杂度是O(n^2)的, 不过本题并没有出那种退化成单链的数据, 所以直接暴力也能水过;

代码:

1 #include 
2 #include
3 using namespace std; 4 5 const int MAXN = 5e4 + 10; 6 struct node{ 7 int c, w, pre; 8 }gel[MAXN]; 9 int num[MAXN];//num[i]为编号为i的绳子当前可以承受的最大重量10 11 int main(void){12 int n, ans = -1;13 scanf("%d", &n);14 for(int i = 0; i < n; i++){15 scanf("%d%d%d", &gel[i].c, &gel[i].w, &gel[i].pre);16 if(ans != -1) continue;17 num[i] = gel[i].c;18 int cnt = i;19 while(cnt != -1){20 num[cnt] -= gel[i].w;21 if(ans == -1 && num[cnt] <= -1) ans = i;22 cnt = gel[cnt].pre;//指向cnt的父亲节点23 }24 }25 if(ans == -1) cout << n << endl;26 else cout << ans << endl;27 return 0;28 }
View Code

 

解法2: 二分 + dfs

很显然在加边的过程中所有边的承受重量都是单调不减的, 那么可以考虑二分答案. 不过要注意判断函数的写法, 每一次判断都需要判断当前 mid条 边组成的树的所有边, 而不能只判断当前 mid 所在链上的边, 显然其他链上也可能存在不合法的边. 判断所有边的话可以 dfs 一遍, 回溯时判断即可.

代码:

1 #include 
2 #include
3 #include
4 #define ll long long 5 using namespace std; 6 7 const int MAXN = 5e4 + 10; 8 struct node{ 9 int c, w, pre;10 }gel[MAXN];11 vector
sol[MAXN];12 bool flag;13 14 ll dfs(int u, int x){15 ll sum = gel[u].w;16 if(u > x) return 0;//mid边后面的不要算上去17 for(int i = 0; i < sol[u].size(); i++){18 sum += dfs(sol[u][i], x);19 }20 if(sum > gel[u].c && u) flag = false;//0是一个虚根,并没有对应的边21 return sum;22 }23 24 int main(void){25 int n;26 scanf("%d", &n);27 for(int i = 1; i <= n; i++){28 scanf("%d%d%d", &gel[i].c, &gel[i].w, &gel[i].pre);29 gel[i].pre++;30 sol[gel[i].pre].push_back(i);31 }32 int l = 1, r = n, cnt = n;33 while(l <= r){34 flag = true;35 int mid = (l + r) >> 1;36 dfs(0, mid);37 if(flag) cnt = mid, l = mid + 1;38 else r = mid - 1;39 }40 printf("%d\n", cnt);41 }
View Code

 

解法3: 并查集

记录每个节点的父节点
然后按输入顺序的倒叙 遍历每一个节点
计算以当前节点为根的子树的重量 ( 因为按照题目的输入顺序来说  当前节点要么没有子节点  要么子树已经遍历完 算入当前树的重量)
当遍历到某个节点时
当前节点与父节点的边无法承载当前节点为根的子树  便从输入序列最晚输入的节点开始删除 
直到与父节点的边的能够承载当前节点为根的子树
又或者已经把遍历过的点都删除完了 
 
这个过程中  用并查集维护某个节点 属于那一个跟节点 并且不断的压缩路径
每个条路径被压缩一次 均摊时间 就是边的数量 所以 这种做法很稳定的 O(n)
 
上面这段话是直接从讨论中复制过来的
代码:
1 #include 
2 #include
3 #include
4 #define ll long long 5 using namespace std; 6 7 const int MAXN = 1e5 + 10; 8 struct node{ 9 ll c, w, p;10 }gel[MAXN];11 12 ll ww[MAXN];13 vector
vt[MAXN];14 int pre[MAXN], sol;15 16 int find(int x){17 return pre[x] == x ? x : pre[x] = find(pre[x]);18 }19 20 void update(int u){21 for(int i = 0; i < vt[u].size(); i++){22 gel[u].w += gel[vt[u][i]].w;23 pre[vt[u][i]] = u;24 }25 while(gel[u].w > gel[u].c){
//u即为当前根节点26 gel[find(sol)].w -= ww[sol];27 sol--;28 }29 }30 31 int main(void){32 int n;33 scanf("%d", &n);34 for(int i = 1; i <= n; i++){35 scanf("%lld%lld%lld", &gel[i].c, &gel[i].w, &gel[i].p);36 gel[i].p++;37 vt[gel[i].p].push_back(i);38 ww[i] = gel[i].w;//后面会对gel操作,所以需要先记录下gel的初始值来39 pre[i] = i;40 }41 sol = n;42 for(int i = n; i > 0; i--){43 update(i);44 }45 printf("%d\n", sol);46 return 0;47 }
View Code

 

转载于:https://www.cnblogs.com/geloutingyu/p/7121750.html

你可能感兴趣的文章
jQuery之end()和pushStack()
查看>>
springboot入门_shiro
查看>>
Bootstrap--响应式导航条布局
查看>>
【好程序员笔记分享】——下拉刷新和上拉加载更多
查看>>
多线程,多进程,协程
查看>>
Hacker News与Reddit的算法比较
查看>>
Learning Python 009 dict(字典)和 set
查看>>
JavaScript中随着鼠标拖拽而移动的块
查看>>
HDU 1021 一道水题
查看>>
进击的 JavaScript(六) 之 this
查看>>
The operation couldn’t be completed. (LaunchServicesError error 0.)
查看>>
php每天一题:strlen()与mb_strlen()的作用分别是什么
查看>>
编程中定义的方法报异常问题
查看>>
使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)
查看>>
工作中收集JSCRIPT代码之(下拉框篇)
查看>>
《转载》POI导出excel日期格式
查看>>
code异常处理
查看>>
git - 搭建最简单的git server
查看>>
.net中从GridView中导出数据到excel(详细)
查看>>
[LeetCode]Single Number II
查看>>