编程之美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*/


0 0
原创粉丝点击