树的直径:(无根)树上最长两点间的最长路径,两次dfs即可,第一次dfs任选一点u,找到距离它最远的点s,再从点s进行一次dfs,找到距离s最远的点t,则s-t之间的路径就是树的直径。证明:
poj2631 树的直径裸题
#include<cstdio> #include<iostream> #include<algorithm> #include<iostream> #include<vector> typedef long long ll; using namespace std; const int maxn=10005; ll d[maxn]; struct KSD { int v,len,next; }g[maxn]; int vis[maxn]; int head[maxn],cnt; void add(int u,int v,int len){ g[++cnt].v=v; g[cnt].next=head[u]; g[cnt].len=len; head[u]=cnt; } void dfs(int x) { vis[x]=1; int i,v; for(i=head[x];i;i=g[i].next) { v=g[i].v; if(vis[v])continue; d[v]+=d[x]+g[i].len; dfs(v); } return ; } int main(){ int a,b,c;int n=0; while(scanf("%d%d%d",&a,&b,&c)!=EOF){ n=max(a,max(b,n)); add(a,b,c); add(b,a,c); } d[1]=0; dfs(1); int m; ll s=0; for(int i=1;i<=n;i++){ vis[i]=0; if(d[i]>s){ m=i; s=d[i]; } d[i]=0; } dfs(m); ll ans=0; for(int i=1;i<=n;i++) ans=max(ans,d[i]); printf("%lld ",ans); }
poj1985 Cow Marathon 求树的直径裸题
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int maxn=200005; int n,m; struct P{ int v,len,next; }g[maxn]; int head[maxn],d[maxn],vis[maxn]; int cnt=0; void add(int u,int v,int len){ g[++cnt].v=v; g[cnt].next=head[u]; g[cnt].len=len; head[u]=cnt; } void dfs(int x) { vis[x]=1; int i,v; for(i=head[x];i;i=g[i].next) { v=g[i].v; if(vis[v])continue; d[v]+=d[x]+g[i].len; dfs(v); } return ; } int main(){ scanf("%d%d",&n,&m); int sum=0; for(int i=1;i<=n-1;i++){ int x,y,z; char c; scanf("%d%d%d %c",&x,&y,&z,&c); // cout<<c<<endl; add(x,y,z); add(y,x,z); // sum+=z; } // sum=sum*2; d[1]=0; dfs(1); int m; int s=0; for(int i=1;i<=n;i++){ vis[i]=0; if(d[i]>s){ m=i; s=d[i]; } d[i]=0; } dfs(m); int ans=0; for(int i=1;i<=n;i++) ans=max(ans,d[i]); // cout<<sum<<" "<<ans<<endl; printf("%d ",ans); }
poj3310 Caterpillar
易知改图是一棵树,首先并查集判断是否联通,先找到直径,再判断所有点距离直径距离
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; const int maxn=605; int fa[maxn],head[maxn],d[maxn],ans[maxn],vis[maxn]; struct P{ int v,len,next; }g[maxn*2]; int mp[maxn][maxn]; int fi(int x){ if(fa[x]==x) return x; return fa[x]=fi(fa[x]); } int cnt=0; void add(int u,int v){ g[++cnt].v=v; g[cnt].next=head[u]; head[u]=cnt; } void dfs(int x) { vis[x]=1; int i,v; for(i=head[x];i;i=g[i].next) { v=g[i].v; if(vis[v])continue; d[v]+=d[x]+1; dfs(v); } return ; } int n,m,p,q,k=0; bool DFS(int z){ if(z==q){ ans[z]=1; return true; } vis[z]=1; for(int i=head[z];i;i=g[i].next){ int v=g[i].v; if(vis[v]) continue; if(DFS(v)){ ans[v]=1; return true; } } return false; } int main(){ int cas=0; while(scanf("%d",&n)&&n!=0){ for(int i=1;i<=n;i++){ d[i]=vis[i]=ans[i]=head[i]=0; fa[i]=i; for(int j=1;j<=n;j++) mp[i][j]=0; } scanf("%d",&m); bool f=1; if(m!=n-1) f=0; for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); mp[x][y]=mp[y][x]=1; x=fi(x);y=fi(y); if(x==y) f=0; fa[x]=y; add(x,y); add(y,x); } for(int i=1;i<=n;i++) if(fi(i)!=fi(1)){ f=0; break; } if(f==0){ printf("Graph %d is not a caterpillar. ",++cas); continue; } dfs(1); int s=0; for(int i=1;i<=n;i++){ vis[i]=0; if(d[i]>s){ p=i; s=d[i]; } d[i]=0; } dfs(p); s=0; for(int i=1;i<=n;i++){ vis[i]=0; if(d[i]>s){ q=i; s=d[i]; } d[i]=0; } DFS(p); for(int i=1;i<=n;i++){ int j=0; if(ans[i]==0){ for(j=1;j<=n;j++) if(ans[j]==1){ if(mp[i][j]==1) break; } if(j>n){ f=0; break; } } } if(f==0){ printf("Graph %d is not a caterpillar. ",++cas); } else printf("Graph %d is a caterpillar. ",++cas); } }
大雪将城镇的街道覆盖了,两辆铲雪机从同一城市出发,要求将所有街道得雪都铲完,任意一辆铲雪机可以铲任意一条街道,最后两辆车可以停在任意一处,问两辆车的最少运动长度
有些道路可以经过一次,但有些街道需要经过两次,那么即找到一条最长的距离,车子只要走一趟,易知这条路径即为直径,故答案为所有边的长度*2-直径
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int maxn=200005; int n,m; struct P{ int v,len,next; }g[maxn]; int head[maxn],d[maxn],vis[maxn]; int cnt=0; void add(int u,int v,int len){ g[++cnt].v=v; g[cnt].next=head[u]; g[cnt].len=len; head[u]=cnt; } void dfs(int x) { vis[x]=1; int i,v; for(i=head[x];i;i=g[i].next) { v=g[i].v; if(vis[v])continue; d[v]+=d[x]+g[i].len; dfs(v); } return ; } int main(){ scanf("%d%d",&n,&m); int sum=0; for(int i=1;i<=n-1;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); sum+=z; } sum=sum*2; d[1]=0; dfs(1); int m; int s=0; for(int i=1;i<=n;i++){ vis[i]=0; if(d[i]>s){ m=i; s=d[i]; } d[i]=0; } dfs(m); int ans=0; for(int i=1;i<=n;i++) ans=max(ans,d[i]); // cout<<sum<<" "<<ans<<endl; printf("%d ",sum-ans); }
poj3099 Go Go Gorelians
给你一张图,已知两两之间的距离为1,问你找出使距离某个点最远距离最小的点,即找到直径,若直径上的点为奇数,则为中间的,若为偶数,则为中间两个,注意输入给的距离是让我们建树的
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> using namespace std; const int maxn=10005; int vis[maxn],head[maxn],cnt,v0[maxn]; struct P{ int v,next; // double len; }g[maxn]; struct Q{ int i,x,y,z; }p[maxn]; int d[maxn],ans[maxn]; //int sum=0; void add(int u,int v){ g[++cnt].v=v; // g[cnt].len=len; g[cnt].next=head[u]; head[u]=cnt; } int ma; int n; void dfs(int x) { vis[x]=1; int i,v; for(i=head[x];i;i=g[i].next) { v=g[i].v; if(vis[v])continue; d[v]+=d[x]+1; dfs(v); } return ; } int q; int l; bool DFS(int z){ if(z==q){ return true; } vis[z]=1; for(int i=head[z];i;i=g[i].next){ int v=g[i].v; if(vis[v]) continue; if(DFS(v)){ ans[++l]=v; return true; } } return false; } int main(){ int c=0; while(scanf("%d",&n)&&n!=0){ l=0; ma=1e7; for(int i=1;i<=1000;i++) vis[i]=head[i]=ans[i]=d[i]=0; c=0; cnt=0; for(int i=1;i<=n;i++){ scanf("%d%d%d%d",&p[i].i,&p[i].x,&p[i].y,&p[i].z); v0[++c]=p[i].i; } for(int i=2;i<=n;i++){ int s=1e9; int t; for(int j=1;j<i;j++){ int o=(p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)+(p[i].z-p[j].z)*(p[i].z-p[j].z); if(o<s){ s=o; t=p[j].i; } } add(t,p[i].i); add(p[i].i,t); } dfs(v0[1]); int c; int s=0; for(int i=1;i<=n;i++){ int k=v0[i]; vis[k]=0; if(d[k]>s){ c=k; s=d[k]; } d[k]=0; } dfs(c); s=0; for(int i=1;i<=n;i++){ int k=v0[i]; vis[k]=0; if(d[k]>s){ q=k; s=d[k]; } d[k]=0; } DFS(c); ans[++l]=c; if(l%2==0){ cout<<min(ans[l/2+1],ans[l/2])<<" "<<max(ans[l/2+1],ans[l/2])<<endl; } else printf("%d ",ans[l/2+1]); } }
树的重心:如果存在某个节点,其所有子树中最大节点的子树最小,则该节点为树的重心;任选一个点作为根,进行dfs,记录某个节点的子节点数,则满足max(n-son[u]-1,sou[v])取最小的节点u即为树的重心(v为u的子节点);
poj3107 Goldfather
树的重心裸题
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=100005; int cnt,d[maxn],vis[maxn],head[maxn],ans[maxn]; struct P{ int v,next; }g[maxn]; void add(int u,int v){ g[++cnt].v=v; g[cnt].next=head[u]; head[u]=cnt; } int ma=1e9; int n; void dfs(int u){ int sum=0; d[u]=1; vis[u]=1; for(int i=head[u];i;i=g[i].next){ int v=g[i].v; if(vis[v]) continue; dfs(v); d[u]+=d[v]; if(d[v]>=ans[u]) ans[u]=d[v]; } if(n-d[u]>ans[u]) ans[u]=n-d[u]; if(ma>ans[u]) ma=ans[u]; } int main(){ cnt=0; scanf("%d",&n); for(int i=1;i<=n;i++){ head[i]=d[i]=vis[i]=0; } for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1); for(int i=1;i<=n;i++){ if(ans[i]==ma) printf("%d ",i); } printf(" "); }
求树的重心,输出字典序最小的
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=100005; int cnt,d[maxn],vis[maxn],head[maxn]; struct P{ int v,next; }g[maxn]; void add(int u,int v){ g[++cnt].v=v; g[cnt].next=head[u]; head[u]=cnt; } int ans1,ans2; int n; void dfs(int u){ int sum=0; d[u]=1; vis[u]=1; for(int i=head[u];i;i=g[i].next){ int v=g[i].v; if(vis[v]) continue; dfs(v); d[u]+=d[v]; sum=max(sum,d[v]); } sum=max(sum,n-d[u]); if((sum<ans2)||(sum==ans2&&u<ans1)){ ans1=u; ans2=sum; } } int main(){ int t; scanf("%d",&t); while(t--){ cnt=0; ans1=1e8; ans2=1e8; scanf("%d",&n); for(int i=1;i<=n;i++){ head[i]=d[i]=vis[i]=0; } for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1); printf("%d %d ",ans1,ans2); } }