• 2016.7.13 树形动规


        RMQ算法讲完没几个小时,我们就开始了小紫中的一节(足见这个算法很重要)——树形动归;

    参考资料:

        1.焦作一中信息学oy(http://www.cnblogs.com/gq-ouyang/archive/2013/02/26/2933431.html

        2.小黄(信息学奥赛一本通)

        3.小紫(算法竞赛入门经典第二版)

    题型总结:(来自资料1

        1、加分二叉树:区间动规+树的遍历;

        2、二叉苹果树:二叉树上的动规;

        3、最大利润:多叉树上的动规;

        4、选课:多叉树转二叉;

        5、选课(输出方案):多叉转二叉+记录路径;

        6、软件安装:判断环+缩点+多叉转二叉;

    学习目标:

        1.熟练掌握树形动规(小紫刷掉,资料一刷掉);

        2.复习树的相关知识;

        (二叉树的分支确定且可以为空,具有良好的递归特性,程序设计很方便,故常将多叉树转二叉树(小黄))

    一.加分二叉树

    题意:树的结构动规(!=树形动规,是根据树的结构的动规)(n<30节点),(分数<100)

    题目分析:分左右,要熟练遍历顺序;老师没让写......代码是资料一里的

    #include<iostream>
    #include<iomanip>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<memory>
    #include<algorithm>
    #include<string>
    #include<climits>
    #include<queue>
    #include<vector>
    #include<cstdlib>
    #include<map>
    using namespace std;
    
    const int ee=50,e=-999999999;
    int n;
    int a[ee]={0},f[ee][ee],root[ee][ee]={0};//f(i,j)中序遍历为i,i+1,…,j的二叉树的最大加分
    
    //**若根节点的下表是k,则左端点的是k-1,右端点是k+1;
    void front(int x,int y)
    {
        if(root[x][y]!=0)
            cout<<root[x][y]<<' ';
        if(root[x][root[x][y]-1]!=0)    front(x,root[x][y]-1);
        if(root[root[x][y]+1][y]!=0)    front(root[x][y]+1,y);
    }
    
    int main()
    {
    //freopen("in.in","r",stdin);
    
    
        //memset 赋初值不能为1 memset(f,1,sizeof(f));
        cin>>n;
        
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
                f[i][j]=1;
        }
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            f[i][i]=a[i];
            root[i][i]=i;
        }
        //区间长度
        for(int len=1;len<=n;len++)
        {
            //区间起点
            for(int i=1;i<=n;i++)
            {
                //终点
                int j=i+len;
                if(j<=n)
                {
                    int temp=e;
                    //因为是中序排列
                    for(int k=i;k<=j;k++)
                    {
                        if(temp < (f[i][k-1]*f[k+1][j]+a[k]))
                        {
                            temp=f[i][k-1]*f[k+1][j]+a[k];
                            root[i][j]=k;
                        }
                    }
                    f[i][j]=temp;
                }
            }
        }
        cout<<f[1][n];
        
        //前序遍历
        cout<<endl;
        front(1,n);
    
        
    fclose(stdin);fclose(stdout);
    return 0;
    }

    二.URAL_1018 二叉苹果树

    题意:每个边(共n)带权值,给定保留边数目(q),算最大权值;1<=Q<= N,1<N<=100

    题目分析:这里用了一个后来都会用到的思想,就是因为边没有点好记,所以可以把边上的分数记在点上,并且给根节点加一条边,这样边数加一以后就不需要分开讨论了;其中createtree递归由上而下不太好理解,我写错了很多次;

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<queue>
    #include<set>
    using namespace std;
    int n,f[105][105],vis[105],sta[105],fin[105],amo[105],q;
    struct node
    {
        int apples,lefloc,rigloc; 
    }thran[105];
    void createtree(int k)
    {
        vis[k]=1;
        for(int i=1;i<=n-1;i++)
        if(sta[i]==k||fin[i]==k)
        {
            int t=sta[i]+fin[i]-k;//被确认的点是子节点 
            if(vis[t])continue;//自上而下判重; 
            createtree(t);//把建树向下传递递归
            if(!thran[k].lefloc)
            {
            thran[k].lefloc=t;//传递节点 
            thran[t].apples=amo[i]; 
            }
            else {//用lefloc就是提醒自己,这个和线段树不一样,直接记节点 
            thran[k].rigloc=t; 
            thran[t].apples=amo[i]; break;}
        }
    }
    void dp(int k)
    {
        if(thran[k].lefloc)
        {
            int lef=thran[k].lefloc;
            int rig=thran[k].rigloc;
            dp(lef);
            dp(rig);
            for(int i=1;i<=q+1;i++)//还可以再简化,因为在除根节点以外不可能取满 
            for(int j=0;j<=i-1;j++)//一边可为0 
            f[k][i]=max(f[k][i],f[lef][j]+f[rig][i-j-1]+thran[k].apples); //必选根 
        } 
        else 
        {
            f[k][1]=thran[k].apples;
            f[k][0]=0;
        }
    } 
    int main()
    {
        freopen("apple.in","r",stdin);
        freopen("apple.out","w",stdout);
        scanf("%d %d",&n,&q);
        for(int i=1;i<=n-1;i++)//n-1条边 
        scanf("%d %d %d",&sta[i],&fin[i],&amo[i]);
        createtree(1);//从1建树确保了后来的上下确定
        dp(1) ;//递归动规 
        cout<<f[1][q+1];
        return 0;
    }

     

  • 相关阅读:
    ie6支持最小高度
    jquery的css详解(二)
    jquery的curCSS方法
    jquery的css详解(一)
    Javascript中的Cookie操作
    C#和Javascript中 正则表达式使用的总结
    SQL JOIN
    C# 委托的”四步走“
    C# 知识笔记
    Jquery学习资源地址
  • 原文地址:https://www.cnblogs.com/SindarDawn/p/5668300.html
Copyright © 2020-2023  润新知