+ Examples algorithm (tree dynamic programming) tree DP (the center of gravity of the tree, the tree farthest distance ...)

I. Introduction:

DP DP of the tree is calculated on the data structure tree.

DP tree has two directions: Leaf -> roots, root -> leaves.

DP tree search implemented by the memory, so a recursive implementation.

Typically time complexity O (n), if the dimension m, compared to O (n * m).

Second, the classic question:

 1. The center of gravity of the tree: http://poj.org/problem?id=1655    leaf -> Root

Each branch is called the focus around the point size of the segment can be more uniform, so that the maximum required minimum size of the branch.

After the completion of the first contribution to any point within a DFS root node, all points are connected subtree size.

As shown below, is the root to perform the DFS, the result is {5, 3, 1, 1, 1}, then the next point is calculated as an arbitrary subtree rooted size 2, only Comparative 2 the direct (size 2 n- node total number of nodes) can be connected and the size of 4,5.

Look at the code.

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int N=2e5+5;

int n;


struct node{               //链式前向星存边
    int from;
    int to;
    int next;
}edge[2*N];
int head[N];
int cnt;
void add(int from,int to)
{
    cnt++;
    edge[cnt].from=from;
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt;
}


int pre[N],sz[N];          //pre用于记录某节点的父节点,sz用于记录某节点所连子树大小。
int dfs(int p,int u)       //随机以某点为根DFS
{
    pre[u]=p;    //记录父节点
    sz[u]=1;
    for(int i=head[u];i!=0;i=edge[i].next)    
    {
        int to = edge[i].to;
        if(to!=p) sz[u] += dfs(u, to);
    }
    return sz[u];
}


int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        cnt=0;
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        memset(pre,0,sizeof(pre));
        memset(sz,0,sizeof(sz));
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x, &y);
            add(x,y);
            add(y,x);
        }
        dfs(0,1);

        int ansV=N;    //记录最大Balance和节点序号
        int ansO=N;
        for(int i=1;i<=n;i++)
        {
            int mx = n - sz[i];    //当以i为中心时唯一需要特殊判断的地方,先设为最大值

            for(int j=head[i];j!=0;j=edge[j].next)    //接下来比较i所有直接连接的子树大小
            {
                int to=edge[j].to;
                if(to!=pre[i]) mx = max(mx, sz[to]);
            }

            if(mx < ansV)    //得到以i为中心时最大balance,与答案比较取小
            {
                ansV = mx;
                ansO = i;
            }
        }
        printf("%d %d\n",ansO,ansV);
    }
}

2. No party boss: http://poj.org/problem?id=2342   leaf -> root

In simple terms it is to give your company a tree structure diagram, each person has a value of joy when a man appeared his direct supervisor can not appear, requiring a total maximum value of joy.

Provided dp [i] [1] indicates the maximum value of the joy i occurs, dp [i] [0] represents the maximum value of the joy i does not occur, a [i] i represents the value of the joy of their own.

Let u is the boss of v, there dp [u] [1] = a [u] + dp [v] [0], dp [u] [0] = max (dp [v] [0], dp [ v] [1]).

From the leaf to the root can be gradually updated.

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int N=6e3+5;

int n;
int dp[N][2],pre[N];    //pre储存某点父节点



int head[N],cnt;    //链式前向星存边
struct nodes{
    int from;
    int to;
    int next;
}edge[N*2];


void Dfs(int node)
{
    for(int i=head[node];i!=0;i=edge[i].next)
    {
        int to=edge[i].to;
        Dfs(to);
        dp[node][1]+=dp[to][0];
        dp[node][0]+=max(dp[to][1],dp[to][0]);
    }
}


int main()
{
    while(cin>>n)
    {
        memset(dp,0,sizeof(dp));
        memset(pre,0,sizeof(pre));
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        cnt=0;


        for(int i=1;i<=n;i++)
        {
            scanf("%d",&dp[i][1]);    //直接把a[i]存入dp[i][1]
        }

        int x,y;
        while(scanf("%d%d",&x,&y)!=-1)
        {
            if(x==0 && y==0) break;

            cnt++;                //有向
            edge[cnt].from=y;
            edge[cnt].to=x;
            edge[cnt].next=head[y];
            head[y]=cnt;

            pre[x]=y;
        }

        int root;
        for(int i=1;i<=n;i++)    //找根节点(根节点上级为0)
        {
            if(pre[i]==0)
            {
                root=i;
                break;
            }
        }
        Dfs(root);
        printf("%d\n",max(dp[root][0],dp[root][1]));
    }

}

Long-winded about, have to see before the former do not exist side chain of direct violence to the star method, able to live to tell the truth because it is the question of data comparing the water, I tried to write a bit, the data used up finish 567ms, while the chain former star style to just 78ms, the gap is still very large, this problem only data 6e3. But from a windfall to see if the magic.

&& is well known that if current conditions in the back will not be executed when a judge sentences condition is satisfied, it should be able to filter out the answer to many of the conditions placed first, to avoid unnecessary waste of time to determine (as usual be able to turn || && should also pay attention to the situation).

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int N=6e3+5;

int n;
int dp[N][2];
int pre[N];
bool vist[N];

void Dfs(int node)
{
    vist[node]=true;
    for(int i=1;i<=n;i++)
    {
        if(pre[i]==node && vist[i]==false)  //如果将if中两个条件调换位置直接超时
        {                                   //能过滤掉较多选项的是第一个条件
            Dfs(i);
            dp[node][1]+=dp[i][0];
            dp[node][0]+=max(dp[i][0], dp[i][1]);
        }
    }
}

int main()
{
    while(cin>>n)
    {
        memset(dp,0,sizeof(dp));
        memset(pre,0,sizeof(pre));
        memset(vist,false,sizeof(vist));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&dp[i][1]);
        }
        int x,y;
        while(scanf("%d%d",&x,&y)!=-1)
        {
            if(x==0 && y==0) break;
            pre[x]=y;
        }
        int root=0;
        for(int i=1;i<=n;i++)
        {
            if(pre[i]==0)
            {
                root=i;
                break;
            }
        }
        Dfs(root);
        printf("%d\n",max(dp[root][1],dp[root][0]));
    }
}

3. The most distant tree: http://acm.hdu.edu.cn/showproblem.php?pid=2196   leaf -> root root && -> leaves

There is simply a tree, each side has a weight, find a point each to reach the most remote.

First, in a tree, to a certain point, it may be most distant from a parent node or child node direction direction.

Provided dp [i] [1] is a parent node i to the direction of the maximum distance, dp [i] [0] is the subnode i direction towards the maximum distance, pre [i] represents the parent node i.

Direction of the child node, the parent node set u and v, have dp [u] [0] = max {dp [v] [0] + w (u, v)}, can be obtained by DFS bottom up.

To parent direction, and the u, v is the sibling nodes have dp [u] [1] = w (u, pre [u]) + max {dp [pre [u]] [1], dp [v] [ 0] + w (pre [u], v)}.

Intuitively, i.e. to the direction of the parent node of the node farthest distance = distance + to the parent node (parent node of the parent node continues to go || direction other sibling child nodes to a parent node down) maximum. Starting from the root to the leaves DFS.

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int N=1e4+5;

int n;

int dp[N][2],prevs[N];


int head[N],cnt;        //链式前向星存边
struct nodes{
    int from;
    int to;
    int value;
    int next;
}edge[2*N];

void Add(int x,int y,int v)
{
    cnt++;
    edge[cnt].from=x;
    edge[cnt].to=y;
    edge[cnt].value=v;
    edge[cnt].next=head[x];
    head[x]=cnt;
}




int Dfs(int node,int pre)        //计算往子节点方向最远距离
{
    for(int i=head[node];i!=0;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to==pre) continue;
        int value=edge[i].value;
        prevs[to]=node;
        dp[node][0]=max(dp[node][0],value+Dfs(to,node));
    }
    return dp[node][0];
}



void Dfs2(int node,int pre)       //计算往父节点方向最远距离
{
    int v=0;               //记录该点到父节点的距离
    dp[node][1]=dp[pre][1];    //混入父节点与兄弟节点的判断

    for(int i=head[pre];i!=0;i=edge[i].next)    //拿出所有兄弟边比较,看看父节点下一步该往哪走
    {
        int to=edge[i].to;
        if(to==prevs[pre]) continue;
        int value=edge[i].value;
        if(to==node) v=value;
        else dp[node][1]=max(dp[node][1], dp[to][0]+value);
    }


    dp[node][1]+=v;          //选完父节点下一步后再把该点到父节点的距离加上去

    for(int i=head[node];i!=0;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to!=pre) Dfs2(to,node);
    }
}


void Origin()    //初始化函数
{
    memset(prevs,0,sizeof(prevs));
    memset(dp,0,sizeof(dp));
    memset(head,0,sizeof(head));
    cnt=0;
    memset(edge,0,sizeof(edge));
}


int main()
{
    while(cin>>n)
    {
        Origin();
        int p,v;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&p,&v);
            Add(i+1,p,v);
            Add(p,i+1,v);
        }
        Dfs(1,0);
        Dfs2(1,0);
        for(int i=1;i<=n;i++)
        {
            printf("%d\n",max(dp[i][0], dp[i][1]));
        }
    }
}

Third, to expand the question:

1. Godfather:http://poj.org/problem?id=3107

Expand the focus of the problem tree.

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int N=5e4+5;

int n;

int mxOfNode[N];

int head[N],cnt=0;
struct node{
    int from;
    int to;
    int next;
}edge[2*N];

void Add(int x,int y)
{
    cnt++;
    edge[cnt].from=x;
    edge[cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}


int pre[N],sz[N];

int Dfs(int prev,int node)
{
    pre[node]=prev;
    sz[node]=1;
    for(int i=head[node];i!=0;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to!=prev) sz[node] += Dfs(node,to);
    }
    return sz[node];
}



int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Add(x,y);
        Add(y,x);
    }
    Dfs(0,1);

    int ansV=N;
    for(int i=1;i<=n;i++)
    {
        int mx = n-sz[i];
        for(int j=head[i];j!=0;j=edge[j].next)
        {
            int to=edge[j].to;
            if(to!=pre[i]) mx = max(mx,sz[to]);
        }
        mxOfNode[i]=mx;
        if(mx<=ansV)
        {
            ansV=mx;
        }
    }
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(mxOfNode[i]==ansV)
        {
            if(cnt==0) printf("%d",i);
            else printf(" %d",i);
            cnt=1;
        }
    }
    printf("\n");

}

2. The more, The Better:http://acm.hdu.edu.cn/showproblem.php?pid=1561

Backpack tree DP

Can overcome the first node as a parent node, a tree is formed, constituting the forest trees.

As the title has a parent node is 0 In this case, we can put all the parent node of the tree in the forest are regarded as 0, so put the forests into a tree.

Set DP [i] [j] denotes the i th node taking the maximum value of the j nodes.

U is set parent node v with dp [u] [j] = max (dp [u] [j], dp [u] [k] + dp [v] [jk])

Bottom-up can be updated.

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int N=205;

int n,m;

int head[N],cnt;
struct nodes{
    int from;
    int to;
    int next;
}edge[2*N];

int dp[N][N];
bool vist[N];

void Origin()
{
    memset(head,0,sizeof(head));
    cnt=0;
    memset(edge,0,sizeof(edge));
    memset(dp,0,sizeof(dp));
    memset(vist,false,sizeof(vist));
}

void Dfs(int node)
{
    vist[node]=true;
    for(int i=head[node];i!=0;i=edge[i].next)
    {
        int to=edge[i].to;
        if(vist[to]==false) Dfs(to);
        for(int j=m;j>=2;j--)
        {
            for(int k=1;k<j;k++)
            {
                dp[node][j]=max(dp[node][j],dp[node][k]+dp[to][j-k]);
            }
        }


    }
}

int main()
{
    while(cin>>n>>m)
    {
        Origin();
        if(n==0 && m==0) break;
        int x,y;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y);

            cnt++;                //单向
            edge[cnt].from=x;
            edge[cnt].to=i;
            edge[cnt].next=head[x];
            head[x]=cnt;

            dp[i][1]=y;
        }
        m++;    //因为我们虚拟多处了一个0节点
        Dfs(0);
        printf("%d\n",dp[0][m]);
    }
}

 

Published 34 original articles · won praise 26 · views 10000 +

Guess you like

Origin blog.csdn.net/sinat_40471574/article/details/100014642