标签:
http://acm.hdu.edu.cn/showproblem.php?pid=4679
给出一棵树,删除该树的某一条边会得到一个值a(该边的energy值)和一个值b(删除该边得到的两棵子树上的最大路径),求删除哪条边可以使得a*b最小,若存在多个最小值,则选择边号较小的那条。
树上所有相邻节点间的距离都为1。
可以知道删除某边后两棵子树的最长路径都是由原树直径的两个端点出发的,以任意一个节点为起点跑一边BFS(或者DFS)找到与其最远的叶子节点v1,再以v1为起点找到距离v1最远的叶子节点v2,v1和v2即原树直径的两个端点。
预处理得到v1、v2和其他所有节点的最短距离后深搜得到以某节点v为根的子树和v1的次大值距离以及和v2的次大距离,再跑一遍树形DP就完事了。
= =看错题还以为energy值就是距离,于是乎写的比较麻烦。
#include<stdio.h> #include<string.h> #include<queue> #define inf 1000000001 #include<stdlib.h> using namespace std; const int N=1e5+1; int Ev[N*2],Enext[N*2],ENo[N*2],Ew[N*2]; int head[N],cnt; int dis[N][2],ori[N][3];/*dis[i][0]代表以节点 i 到其叶子的最大距离,dis[i][1]则表示以节点 i 为根的子树的最长路径,ori则代表相对应的节点*/ int dp1[N][2],dp2[N][2],res1,res2;//dp1[i][]代表以节点i为根的子树到直径两个端点的最大值和次大值 bool mark[N]; int a[N][2];//直径左端点和其他所有点的最短距离以及直径右端点和其他所有点的最短距离 void clean(int n) { for(int i=1;i<=n;++i) head[i]=-1; cnt=0; } int mmax(int a,int b) { return a>b?a:b; } int mmin(int a,int b) { return a<b?a:b; } void addedge(int u,int v,int w,int No) { Ev[cnt]=v; Ew[cnt]=w; ENo[cnt]=No; Enext[cnt]=head[u]; head[u]=cnt++; } void Dfs_FindDis(int id,int u) { int max1=0,max2=0; int v1,v2; for(int i=head[id];i!=-1;i=Enext[i]) { int v=Ev[i]; int w=1; if(Ev[i]==u)continue; Dfs_FindDis(Ev[i],id); int disv=dis[v][0]+w; if(disv>=max1) { max2=max1,v2=v1; max1=disv,v1=ori[v][0]; } else if(disv>max2) { max2=disv,v2=ori[v][0]; } disv=dis[v][1]; if(disv>dis[id][1]) { dis[id][1]=disv; ori[id][1]=ori[v][1]; ori[id][2]=ori[v][2]; } } if(!max1)ori[id][0]=ori[id][1]=ori[id][2]=id; else { ori[id][0]=v1; dis[id][0]=max1; if(max2) { if(max1+max2>dis[id][1]) { dis[id][1]=max1+max2; ori[id][1]=v1; ori[id][2]=v2; } } else { if(max1>dis[id][1]) { dis[id][1]=max1; ori[id][1]=v1; ori[id][2]=id; } } } } struct node { int x; int c; friend bool operator<(node a,node b) { return a.c>b.c; } }; void Opt_Dijkstra(int start,int y) { priority_queue<node>q; node f,m; while(!q.empty())q.pop(); f.x=start,f.c=a[start][y]=0; q.push(f); while(!q.empty()) { f=q.top(),m=f,q.pop(); if(mark[f.x])continue; if(f.c!=a[f.x][y])continue; mark[f.x]=true; for(int i=head[f.x];i!=-1;i=Enext[i]) { int pos=Ev[i]; if(!mark[pos]) { f=m; int w=1; int p=f.c+w; if(p<a[pos][y]) { a[pos][y]=p; f.x=pos,f.c=p; q.push(f); f=m; } } } } } void Dfs1(int id,int fa,int x,int y) { int m1=a[id][0],s1=a[id][0],m2=a[id][1],s2=a[id][1]; for(int i=head[id];i!=-1;i=Enext[i]) { int v=Ev[i]; if(v==fa)continue; Dfs1(v,id,x,y); if(dp1[v][0]>=m1) { if(m1!=0)s1=m1; m1=dp1[v][0]; } else if(dp1[v][1]>s1)s1=dp1[v][1]; if(dp2[v][0]>=m2) { if(m2!=0)s2=m2; m2=dp2[v][0]; } else if(dp2[v][1]>s2)s2=dp2[v][1]; } if(m1==0)dp1[id][0]=dp1[id][1]=a[id][0]; else { dp1[id][0]=m1; dp1[id][1]=s1; } if(m2==0)dp2[id][0]=dp2[id][1]=a[id][1]; else { dp2[id][0]=m2; dp2[id][1]=s2; } } void Dfs2(int id,int fa,int smax1,int smax2,int x,int y) { smax1=mmax(smax1,dp1[id][1]); smax2=mmax(smax2,dp2[id][1]); for(int i=head[id];i!=-1;i=Enext[i]) { int v=Ev[i]; if(v==fa)continue; Dfs2(v,id,smax1,smax2,x,y); int cost; if(ori[v][0]==x) cost=mmax(smax2,dis[v][1])*Ew[i]; else if(ori[v][0]==y) cost=mmax(smax1,dis[v][1])*Ew[i]; else cost=a[x][1]*Ew[i]; if(cost==res1) { res1=cost; res2=mmin(res2,ENo[i]); } else if(cost<res1) { res1=cost; res2=ENo[i]; } } } void solve() { int T,n,x,y,Case=0; int c; scanf("%d",&T); while(T--) { scanf("%d",&n); clean(n); for(int i=1;i<=n;++i) { a[i][0]=a[i][1]=inf,mark[i]=false; } for(int i=1;i<n;++i) { scanf("%d%d%d",&x,&y,&c); addedge(x,y,c,i); addedge(y,x,c,i); } memset(dis,0,sizeof(dis)); Dfs_FindDis(1,1); x=ori[1][1]; y=ori[1][2]; memset(mark,false,sizeof(mark)); Opt_Dijkstra(x,0); memset(mark,false,sizeof(mark)); Opt_Dijkstra(y,1); res1=inf; res2=n+1; Dfs1(1,1,x,y); Dfs2(1,1,dp1[1][1],dp2[1][1],x,y); printf("Case #%d: %d\n",++Case,res2); } } int main() { solve(); return 0; }
HDU 4679:Terrorist’s destroy 树形DP
标签:
原文地址:http://www.cnblogs.com/kiuhghcsc/p/5726224.html