HDU 3721 Building Roads

来源:互联网 发布:快到期已备案域名购买 编辑:程序博客网 时间:2024/05/21 15:47

题意:

一棵树中移动一条边使树的直径尽量小


思路:

首先枚举移动了哪条边

然后树被分成两个子树  分别求两个子树的直径

接着对于每棵子树分别找出一个在直径上的点  使得直径被分成的两部分中长的部分尽量短(下文简称半直径)

那么  移动这条边所形成的树的直径  要么是两棵子树的直径  要么是两半直径和移动边长的和

最后对所有的新树的直径求最小值即为答案


代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 2510int T,t,n,tot,ans,loc;struct edge{int u,v,w,next;}ed[N*2];int head[N],dis[N],vis[N],pre[N],mid[2];void add(int u,int v,int w){ed[tot].u=u; ed[tot].v=v; ed[tot].w=w; ed[tot].next=head[u]; head[u]=tot++;}void color(int u,int fa,int col){vis[u]=col;for(int i=head[u];~i;i=ed[i].next){int v=ed[i].v;if(v!=fa) color(v,u,col);}}void dfs1(int u,int fa){for(int i=head[u];~i;i=ed[i].next){int v=ed[i].v;if(v!=fa){dis[v]=dis[u]+ed[i].w;if(dis[loc]<dis[v]) loc=v;dfs1(v,u);}}}void dfs2(int u,int fa,int col){for(int i=head[u];~i;i=ed[i].next){int v=ed[i].v;if(v!=fa&&vis[v]==col){dis[v]=dis[u]+ed[i].w;pre[v]=u;if(dis[loc]<dis[v]) loc=v;dfs2(v,u,col);}}}int getf(int u,int fa,int col){int i;loc=u;dfs1(u,fa);for(i=0;i<n;i++){if(vis[i]==col) dis[i]=0;}dfs2(loc,fa,col);for(i=loc,mid[col]=dis[loc];~pre[i];i=pre[i]){mid[col]=min(mid[col],max(dis[loc]-dis[i],dis[i]));}return dis[loc];}int main(){int i,u,v,w;scanf("%d",&T);for(t=1;t<=T;t++){scanf("%d",&n);tot=0;memset(head,-1,sizeof(head));for(i=1;i<n;i++){scanf("%d%d%d",&u,&v,&w);add(u,v,w);add(v,u,w);}ans=N*N;for(i=0;i<tot;i+=2){color(ed[i].u,ed[i].v,0);color(ed[i].v,ed[i].u,1);memset(dis,0,sizeof(dis));memset(pre,-1,sizeof(pre));ans=min(ans,max(max(getf(ed[i].u,ed[i].v,0),getf(ed[i].v,ed[i].u,1)),mid[0]+mid[1]+ed[i].w));//printf("%d %d %d\n",mid[0],mid[1],ans);}printf("Case %d: %d\n",t,ans);}return 0;}


0 0
原创粉丝点击