分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
主要是利用了反证法:
假设 s-t这条路径为树的直径,或者称为树上的最长路
现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路
证明:
1 设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则
dis(u,T) >dis(u,s) 且 dis(u,T)>dis(u,t) 则最长路不是s-t了,与假设矛盾
2 设u不为s-t路径上的点
首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了
所以现在又有两种情况了:
1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)
2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,
则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾
附上一张第二种情况的图
- #include <cstdio>
- #include <iostream>
- #include <fstream>
- #include <cstring>
- #include <string>
- #include <vector>
- #define OP(s) cout<<#s<<"="<<s<<" ";
- #define PP(s) cout<<#s<<"="<<s<<endl;
- using namespace std;
- int n;
- vector <int> adj[20010];
- int son[20010];
- bool vd[20010];
- int ans,asize = 1<<29;
- void DFS(int s)
- {
- vd[s] = 1;
- son[s] = 0;
- int blance = 0;
- int size = adj[s].size();
- for (int j = 0;j < size;j++)
- {
- int u = adj[s][j];
- if (vd[u]) continue;
- DFS(u);
- son[s] += son[u]+1;
- blance = max(blance,son[u]+1);
- }
- blance = max(blance,n - son[s] - 1);
- if (blance < asize || blance == asize && s < ans)
- ans = s,asize = blance;
- }
- int main()
- {
- // freopen("test.txt","r",stdin);
- int T;
- cin>>T;
- while(T--)
- {
- cin>>n;
- for (int i = 1;i <= n;i++) adj[i].clear();
- for (int i = 1;i <= n-1;i++)
- {
- int u,v;
- scanf("%d%d",&u,&v);
- adj[u].push_back(v);
- adj[v].push_back(u);
- }
- memset(vd,0,sizeof(vd));
- asize = 1<<29;
- DFS(1);
- cout<<ans<<" "<<asize<<endl;
- }
- return 0;
- }
题意:给定一棵N(1<= N <=10000)个结点的带权树,定义dist(u,v)为u,v两点间的最短路径长度,路径的长度定义为路径上所有边的权和。再给定一个 K ,如果对于不同的两个结点a,b,如果满足dist(a,b) <=K,则称(a,b)为合法点对。求合法点对个数。
思路:看了论文《分治算法在树的路径问题中的应用》,里面讲解的很清楚,一条路径要么过根节点,要么在一颗子树中,所以用分治算法。找到树的重心作为根节点,这样每次树的节点数至少减少一半。处理经过当前根节点路径<=k的点对数,然后把根节点去掉后就把原来的树分成几颗子树了,再处理子树。我们在求经过一个根节点的路径时,里面还包含了点对属于同一颗子树的情况,所以要去掉这部分的点。
dis(i)+dis(j)<=k(i,j的父节点不为根节点的同一个儿子)
=dis(i)+dis(j)<=k-dis(i)+dis(j)<=k(i,j的父节点属于根节点的同一儿子).
- #include <algorithm>
- #include<stdio.h>
- #include<string.h>
- const int N=10010;
- using namespace std;
- int head[N],num,f[N],son[N],n,D,root,size,ans,dis[N],d[N],cum;
- bool vis[N];
- #define max(a,b) (a<b?b:a)
- struct edge
- {
- int st,ed,w,next;
- }e[N*2];
- void addedge(int x,int y,int w)
- {
- e[num].st=x;e[num].ed=y;e[num].w=w;e[num].next=head[x];head[x]=num++;
- e[num].st=y;e[num].ed=x;e[num].w=w;e[num].next=head[y];head[y]=num++;
- }
- void getroot(int u,int father)//求树的重心
- {
- int i,v;
- f[u]=0;son[u]=1;
- for(i=head[u];i!=-1;i=e[i].next)
- {
- v=e[i].ed;
- if(vis[v]||v==father)continue;
- getroot(v,u);
- son[u]+=son[v];
- f[u]=max(f[u],son[v]);
- }
- f[u]=max(f[u],size-son[u]);
- if(f[u]<f[root])root=u;
- }
- void getdis(int u,int father)//求节点到根节点的距离
- {
- int i,v;
- son[u]=1;//更新子树的节点的子节点数,不更新也能ac
- d[cum++]=dis[u];//将点到根节点的距离加入数组
- for(i=head[u];i!=-1;i=e[i].next)
- {
- v=e[i].ed;
- if(vis[v]||v==father)continue;
- dis[v]=dis[u]+e[i].w;
- getdis(v,u);
- son[u]+=son[v];
- }
- }
- int cont(int u,int mit)
- {
- int res=0,L,R;
- dis[u]=mit;
- cum=0;
- getdis(u,0);
- sort(d,d+cum);//将点到根节点的距离排序
- for(L=0,R=cum-1;L<R;)
- {
- if(d[L]+d[R]<=D)//如果d[L]+d[R]<=D,L代表的节点可以与(R-L)个节点成对
- res+=(R-L++);
- else R--;
- }
- return res;
- }
- void work(int u)
- {
- int i,v;
- vis[u]=true;
- ans+=cont(u,0);//路径经过该根节点的点对数
- for(i=head[u];i!=-1;i=e[i].next)
- {
- v=e[i].ed;
- if(vis[v])continue;
- ans-=cont(v,e[i].w);//减去属于v子树的点对数
- root=0;f[root]=size=son[v];
- getroot(v,0);//求v子树的根节点
- work(r
给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow