• hdu5834(树形dp,dfs两次规划)


    src:http://acm.hdu.edu.cn/showproblem.php?pid=5834

     题意:给定一棵树,有点权和边权,点权上的价值只能取一次,多次通过边要花费多次边权,求以每个节点为起点能获得的最大价值。

    思路:第一次dfs,得到各节点往下的最大价值,然后第二次dfs考虑父节点的影响,将无根树转化成有根树考虑,实现方式有多种!!!

    我的实现方式在求次优路有点麻烦,我是通过第二次dfs每个节点时比较每个子节点返回和不返回的差来得到 最优点 和 次优点 ,而下面这个别人的博客的代码是直接在第一次dfs时就算出次优路(但是这样没算出次优点是哪个,当然题目也没要求),代码简短了许多!

    网上代码:https://www.cnblogs.com/lthb/p/5890367.html

    我的ac代码:

    #include<bits/stdc++.h>
    #define per(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    typedef long long ll;
    const int mod=1000000007;
    const int inf =0x3f3f3f3f;
    const double eps=1e-8;
    #define siz 100005
    int n,head[siz],Enum=0,val[siz],ans[siz],dp[siz][2],G[siz][2];//0 is not back; 1 is back
    //int no_back[siz];
    struct Edge{int to,w,ne;}edge[siz*2];
    bool vis[siz];
    void add_edge(int a,int b,int c){
        edge[Enum].to=b;
        edge[Enum].w=c;
        edge[Enum].ne=head[a];
        head[a]=Enum++;
    }
    void init()
    {
        memset(head,-1,sizeof(head));
        Enum=0;
        memset(ans,0,sizeof(ans));
    }
    void dfs(int u,int fa)
    {
        dp[u][0]=dp[u][1]=val[u];
        for(int i=head[u];i!=-1;i=edge[i].ne){
            int v=edge[i].to;
            if(v==fa)continue;
            dfs(v,u);
            dp[u][0]=max(dp[u][0],max(dp[u][0]+dp[v][1]-edge[i].w*2,dp[u][1]+dp[v][0]-edge[i].w));
            dp[u][1]=max(dp[u][1],dp[u][1]+dp[v][1]-edge[i].w*2);
        }
    }
    void dfs2(int u,int fa,int Cost,int fa_max_node,int fa_sec_node,int fa_sec_val)
    {
        int mm=0,max_node=-1,sec_node=-1,max_val=0,sec_val=0,bk,nbk;//bk表示回,nbk表示不回 通过这两个相减来比较出最大边和次大边
        for(int i=head[u];i!=-1;i=edge[i].ne){
            int v=edge[i].to;
            //if(v==fa)continue; //不返回的也可能是父节点
            if(v==fa){
                int tmp0=dp[fa][0],tmp1=dp[fa][1];//然后减去本节点的影响
                if(u==fa_max_node){//这里tmp0不能用dp减,因为dp已经更新过了,算上父节点了
                    tmp0-=(G[u][0]-Cost);
                    if(fa_sec_node!=-1)tmp0+=fa_sec_val;
                    if(G[u][1]-Cost*2 > 0)tmp1-=(G[u][1]-Cost*2);//理由同下
                }
                else if(u!=fa_max_node){
                    if(G[u][1]-Cost*2 > 0){int ttt=G[u][1]-Cost*2;tmp0-=ttt;tmp1-=ttt;}
                }
                bk=tmp1-Cost*2;
                nbk=tmp0-Cost;
            }
            else if(v!=fa){
                bk=dp[v][1]-edge[i].w*2;
                nbk=dp[v][0]-edge[i].w;
            }
            if(nbk>0 && nbk-max(0,bk) > max_val){//注意这里要选用0和bk的最大值,因为nbk可能是负数,
                sec_node=max_node;  //然后考虑的是算上这个子树的nbk的贡献是否比其他子树的nbk-bk的大
                sec_val=max_val;    //大的话就要考虑加上这个nbk了
                max_node=v;
                max_val=nbk-max(0,bk);
            }
            else if(nbk>0 && nbk-max(0,bk) >sec_val){//成不了最大权路径就考虑次大权路径
                sec_node=v;
                sec_val=nbk-max(0,bk);
            }
        }
        for(int i=head[u];i!=-1;i=edge[i].ne){
            int v=edge[i].to;
            if(v==fa)continue;
            int tmp0=dp[u][0],tmp1=dp[u][1];//然后减去节点v的影响
            if(v==max_node){//v是max_node,则说明nbk是大于0的而不能说明back是大于零的,还需要判断一下
                tmp0-=(dp[v][0]-edge[i].w);
                if(sec_node!=-1)tmp0+=sec_val;
                if(dp[v][1]-edge[i].w*2 > 0)tmp1-=(dp[v][1]-edge[i].w*2);
    //            dp[v][0]=max(dp[v][0],dp[v][1]+tmp0-edge[i].w);
    //            dp[v][0]=max(dp[v][0],dp[v][0]+tmp1-edge[i].w*2);//!!!注意:这样写就加两遍啦!!!
                dp[v][0]=max(dp[v][0],max(dp[v][1]+tmp0-edge[i].w,dp[v][0]+tmp1-edge[i].w*2));
                dp[v][1]=max(dp[v][1],dp[v][1]+tmp1-edge[i].w*2);
            }
            else if(v!=max_node){
                if(dp[v][1]-edge[i].w*2 > 0){int ttt=dp[v][1]-edge[i].w*2;tmp0-=ttt;tmp1-=ttt;}
                dp[v][0]=max(dp[v][0],max(dp[v][0]+tmp1-edge[i].w*2,dp[v][1]+tmp0-edge[i].w));
                dp[v][1]=max(dp[v][1],dp[v][1]+tmp1-edge[i].w*2);
            }
            dfs2(v,u,edge[i].w,max_node,sec_node,sec_val);
        }
    }
    
    int main()
    {
        int T,cas=0;
        scanf("%d",&T);
        while(T--){
            cas++;
            int a,b,c;
            scanf("%d",&n);
            init();
            per(i,1,n)scanf("%d",&val[i]);
            per(i,1,n-1){scanf("%d %d %d",&a,&b,&c);add_edge(a,b,c);add_edge(b,a,c);}
            dfs(1,-1);
            per(i,1,n){G[i][0]=dp[i][0];G[i][1]=dp[i][1];}
            dfs2(1,-1,0,0,0,0);
            printf("Case #%d:
    ",cas);
            per(i,1,n)printf("%d
    ",dp[i][0]);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    二分查找
    泛型 学习
    一个时间转换的功能的类
    TCP/IP、Http、Socket的区别
    值得学习的
    popupwindow使背景变暗
    设计模式最好的学习网站
    观察者模式
    数据报的使用
    网络编程server
  • 原文地址:https://www.cnblogs.com/WindFreedom/p/9443506.html
Copyright © 2020-2023  润新知