poj3585(树形dp,换根法)

换根法思想为,

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\n",ans);
    }
return 0;
}

 

posted @ 2018-08-04 16:03  lmjer  阅读(1379)  评论(1编辑  收藏  举报