编程之美2013 树上的三角形
来源:互联网 发布:mac远程控制 向日葵 编辑:程序博客网 时间:2024/05/21 07:14
可以过大数据的解法:
题意:一棵树上每条边有一个长度,问从树上A点到B点的最短路径(唯一)所经过的长度能否组成一个三角形
解题思路:
数据范围:结点数:1 ≤ N ≤ 100000, 询问数:1 ≤ M ≤ 100000, 每条边的长度:1 ≤ len ≤ 1000000000;
其实关键是判断是否能组成三角形,O(n)就行,对于排序后的结点a[i],只要找到比a[i]小的两个结点a[j],a[k]并且a[j]+a[k]>a[i],那么a[j]就取a[i-2],a[k]就取a[i-1];另外还有一个关键点就是如果长度数组刚好不满足组成三角形的条件,且最大数要最小,那么就是一个斐波那契数列,1,1,2,3,5,8,...,可以知道斐波那契数列的第50个是比len的最大长度大,所以一旦需要判断能否组成三角形的长度数>=50,那么不用判断也知道肯定可以组成三角形。从而优化效率。
另外还要优化bfs,因为对于一个点作为中心,然后向周围不断扩散的那种树,就有可能bfs要O(n)的复杂度,所以要把bfs优化为O(50)。就可以先bfs整棵树,以1号结点为根结点,深度为0,然后记录每个结点的深度以及父结点。然后
对于结点s,t,根据深度,先将s,t提升到同一深度,然后不断向父结点靠(记录边的长度),直到相遇,那么就可以保证是O(50)的复杂度了。
所以对于每个询问(a,b),先bfs记录深度以及每个结点的父结点( O(n) ),然后每次询问可以用 O(ClogC) 解决(C=50),
总复杂度就是 O( n+m*(Clog(C)) )
参考资料:http://blog.csdn.net/xiaozhuaixifu/article/details/9325101
#include<stdio.h>#include<string.h>#include<stdlib.h>#include<math.h>#include<iostream>#include<algorithm>#include<queue>#include<stack>#include<vector>#include<map>#include<set>#include<deque>#include<bitset>using namespace std;int f[100010][2],deep[100010];vector<int> e[100010];int c[60], cn;void dfs(int fa,int x,int d){deep[x]=d;for(int i=0;i<(int)e[x].size();i+=2){int y=e[x][i];if(y==fa)continue;f[y][0]=x;//father结点f[y][1]=e[x][i+1];//父结点与当前结点的长度dfs(x,y,d+1);}}int main(){int T,ca=1;scanf("%d",&T);while(T--){int n;scanf("%d",&n);for(int i=1;i<=n;i++)e[i].clear();for(int i=1;i<n;i++){int a,b,len; scanf("%d%d%d",&a,&b,&len);e[a].push_back(b);e[a].push_back(len);e[b].push_back(a);e[b].push_back(len);}dfs(0,1,0);//以1号结点为根结点,深度为0,然后记录每个结点的深度以及父结点f[1][0]=0; printf("Case #%d:\n", ca++);int m;scanf("%d",&m);while(m--){int s,t;cn=0;scanf("%d%d",&s,&t);if(s==1 || t==1){int tmp=(s==1)?t:s;s=1;t=tmp;while(f[t][0]!=0){c[cn++]=f[t][1];t=f[t][0];if(cn>=50)break;}}else {//先将s,t提升到同一深度,然后不断向父结点靠,直到相遇while(deep[t]>deep[s]){c[cn++]=f[t][1];t=f[t][0];if(cn>=50)break;}while(deep[s]>deep[t]){c[cn++]=f[s][1];s=f[s][0];if(cn>=50)break;}while(s!=t){c[cn++]=f[s][1];s=f[s][0];c[cn++]=f[t][1];t=f[t][0];if(cn>=50)break;}}if(cn>=50){printf("Yes\n");continue;}sort(c,c+cn);int flag=0;for(int i=0;i+2<cn;i++){if(c[i]+c[i+1]>c[i+2]){flag=1;break;}}if(flag)printf("Yes\n");else printf("No\n");}}}/*input:251 2 51 3 202 4 304 5 1523 43 551 4 322 3 1003 5 454 5 6021 41 3output:Case #1:NoYesCase #2:NoYes*/
P.S.之前的解法(仅能过小数据):
题意:一棵树上每条边有一个长度,问从树上A点到B点的最短路径(唯一)所经过的长度能否组成一个三角形
解题思路:数据范围:结点数:1 ≤ N ≤ 100000, 询问数:1 ≤ M ≤ 100000, 每条边的长度:1 ≤ len ≤ 1000000000;
其实关键是判断是否能组成三角形,O(n)就行,对于排序后的结点a[i],只要找到比a[i]小的两个结点a[j],a[k]并且a[j]+a[k]>a[i],那么a[j]就取a[i-2],a[k]就取a[i-1];另外还有一个关键点就是如果长度数组刚好不满足组成三角形的条件,且最大数要最小,那么就是一个斐波那契数列,1,1,2,3,5,8,...,可以知道斐波那契数列的第50个是比len的最大长度大,所以一旦需要判断能否组成三角形的长度数>=50,那么不用判断也知道肯定可以组成三角形。从而优化效率。
所以对于每个询问(a,b),直接bfs(a,b),bfs里面加上上面的优化,就可以了。
#include<stdio.h>#include<string.h>#include<stdlib.h>#include<math.h>#include<iostream>#include<algorithm>#include<queue>#include<stack>#include<vector>#include<map>#include<set>#include<deque>#include<bitset>#define inf (1<<30)#define N 100005using namespace std;struct node{ int num; vector<int>len;};vector<pair<int,int> >Map[N];// to, lenbool flag[N];bool bfs(int start,int end){ if(start==end) return false; memset(flag,false,sizeof(flag)); queue<node>Q; node p,q; p.num=start; p.len.clear(); Q.push(p); flag[start]=true; while(!Q.empty()) { q=Q.front(); Q.pop(); for(int i=0;i<Map[q.num].size();i++) { if(flag[Map[q.num][i].first]==false) { flag[Map[q.num][i].first]=true; p.num=Map[q.num][i].first; p.len=q.len; p.len.push_back(Map[q.num][i].second); if(p.len.size()>=50) return true;// bfs达到50个,说明start->end肯定也>=50个,说明一定可以组成三角形 if(p.num==end) { if(p.len.size()<3) return false; sort(p.len.begin(),p.len.end()); for(int j=2;j<p.len.size();j++)// O(n)判断能否组成三角形 { if(p.len[j-2]+p.len[j-1]>p.len[j]) { return true; } } return false; } Q.push(p); } } } return false;}int main(){ int i,j,k; int n,m,t; scanf("%d",&t); for(int tcase=1;tcase<=t;tcase++) { scanf("%d",&n); for(i=0;i<=n;i++) Map[i].clear(); pair<int,int>get; for(i=1;i<n;i++) { int from,to,len; scanf("%d%d%d",&from,&to,&len); get=make_pair(to,len); Map[from].push_back(get); get=make_pair(from,len); Map[to].push_back(get); } printf("Case #%d:\n",tcase); scanf("%d",&m); while(m--) { int from,to; scanf("%d%d",&from,&to); if(bfs(from,to)==true) { printf("Yes\n"); } else printf("No\n"); } }}/*input:251 2 51 3 202 4 304 5 1523 43 551 4 322 3 1003 5 454 5 6021 41 3output:Case #1:NoYesCase #2:NoYes*/
- 编程之美2013 树上的三角形
- 编程之美--树上的三角形
- 2013编程之美资格赛【树上的三角形】
- 微软2013 编程之美 第三题 树上的三角形
- 2013编程之美全国挑战赛-树上的三角形
- 2013编程之美 资格赛 树上的三角形
- 2013微软编程之美 资格赛C 树上的三角形
- 编程之美资格赛第三题:树上的三角形
- 编程之美大赛第三题 树上的三角形 .
- 编程之美热身赛 题目3 : 树上的三角形
- 2013编程之美资格赛之树上的三角形(Java实现)
- 编程之美 2013 全国挑战赛 资格赛 题目三 树上的三角形
- 编程之美热身赛——树上三角形(解决RE Runtime Error)
- Qual_C:树上的三角形
- 练习 树上的三角形
- 树上的三角形
- Qual_C:树上的三角形
- 读书笔记之编程之美 - 4.8 三角形测试用例
- [连载]C#程序设计(09)--- 类和对象
- mpc5125的lpc总线
- 统一资源管理与调度平台(系统)介绍
- 读一组整数到vector 对象,计算并输出每对相邻元素的和。如果读入元素个数 为奇数,则提示用户最后一个元素没有求和,并输出其值。然后修改程序:头 尾元素两两配对(第一个和最后一个,第二个和倒数第二个
- Prim算法
- 编程之美2013 树上的三角形
- 【作业检查】第4周情况汇总(计科1111-1114),第5周作业周日早8点前公布
- Windows内存管理
- Android NDK: jni/Android.mk: Cannot find module with tag 'cocos2dx' in import path
- 成名的代价
- 详解RelativeLayout相对布布局的属性
- 一个不需要摄像装备在家也能实现的全景演示效果
- 【周末技术总结】git
- HDU 3339 In Action 价值为最短路的背包