换根法思想为,
1,随便找一个点作为根进行dp,
2,再以原来点为根进行dp,此次dp,设最优解为 f[x],那么f[root]=d[root],这是显而易见的
然后再通过找d[son]与f[x]之间关系进行dp
比如本道题,若f[x]已知最优解,那么把son换成根,f[x]的最优解即为 d[v]+f[x]-min(d[v],w(x,v))
类似点分治求重心,通过与父亲值做减法,求出树上除v子树外最优值
&&&&&此种类型适用于,给出一棵树,要以每个点为根做一次dp的题目
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct my{ int v; int next; int w; }; const int maxn=500000+10; int adj[maxn],fa,dp[maxn],root,du[maxn],f[maxn],ans; my bian[maxn*2]; bool vis[maxn]; void myinsert(int u,int v,int w){ bian[++fa].v=v; bian[fa].next=adj[u]; adj[u]=fa; bian[fa].w=w; } void dfs1(int x){ vis[x]=true; //dp[x]=0; for (int i=adj[x];i;i=bian[i].next){ int v=bian[i].v; if(!vis[v]){ dfs1(v); if(du[v]!=1) dp[x]+=min(bian[i].w,dp[v]); else dp[x]+=bian[i].w; } } } void dfs2(int x){ vis[x]=true; for (int i=adj[x];i;i=bian[i].next){ int v=bian[i].v; if(!vis[v]){ if(du[x]!=1) f[v]=dp[v]+min(f[x]-min(dp[v],bian[i].w),bian[i].w); else { f[v]=dp[v]+bian[i].w; // printf("%d ",x); }//此时x为根 ans=max(f[v],ans); dfs2(v); } } } int main(){ int t,u,v,w,n; scanf("%d",&t); while(t--){ fa=0; ans=0; memset(vis,0,sizeof(vis)); memset(bian,0,sizeof(bian)); memset(dp,0,sizeof(dp)); memset(du,0,sizeof(du)); memset(adj,0,sizeof(adj)); memset(f,0,sizeof(f)); scanf("%d",&n); for (int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); du[u]++; du[v]++; myinsert(u,v,w); myinsert(v,u,w); } root=1; dfs1(root); memset(vis,0,sizeof(vis)); f[root]=dp[root]; dfs2(root); printf("%d ",ans); } return 0; }