Dp touch the tree blind course

Tree \ (dp \) touch blind course

Preface:

  • What is a tree \ (dp \) ?

Briefly, DP tree, is the dynamic programming in a tree structure, since the tree structure having certain characteristics, more complex relationships can be described, together with a recursive definition of the tree, is a very suitable movable Regulation frame, the movable part of the regulation of very special type.

  • How to achieve tree \ (dp \) ?

    Dp tree representation of the state, usually the first node number (on behalf of the node to the root of the subtree), most of the time, we use a recursive manner to achieve dynamic programming tree. For each node x, we first recursively all child nodes of x and dp on its children, in the back, state transition from a child node to node x.

  • Tree \ (dp \) classification

    • Select the node class: Given a tree, each node has a certain value, let you choose a tree any number of points, but to meet certain parent-child relationship (for example, \ (FA \) and \ (son \ ) to co-exist, or can not coexist), find the maximum or minimum contribution.

    Common dp routines:

    定义状态:f[i][0/1]表示在以i号节点为根的子树中,i号节点是(1)否(1)选择所能造成的最大或最小贡献.
    • Packet Backpack: Given a tree, or a tree node to have certain values, let you choose a tree a limited number of points or edges , find the maximum or minimum contribution.
    定义状态:f[i][j]表示在以i号节点为根的子树中,选择j个节点或边所得的最大值或最小值
    • These two are more common, but far more than the above two categories tree dp, it will be mentioned some other type.
  • Recursive method of tree dp

    • For unrooted trees, i.e., subject to no mandatory edges, can be used as templates recursion:
    inline void dfs(int x,int fa)//fa是x的父亲节点
    {
      for(int i=head[x];i;i=Next[i])//邻接表常规便利
      {
          int y=ver[i];
          if(y==fa) continue;
          dfs(y,x);
          /*dp具体内容*/
      }
    }
  • For rooted tree, i.e., from the surface of the problem, we can easily see that there is a directed graph (such as "No boss Ball"), in this case we can add two sides, it as unrooted trees with of the template to handle, or in the original directed graph to find a root, then this recursive.

inline void dfs(int x)
{
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        dfs(y);
        //dp
    }
}//找根可以用其入度为零这一特点来找,这里不再介绍。

Selection of examples:

Note: The code given hereinafter only the core part, related to the basic function will be given here

Plus side adjacency table functions:

inline void add(int x,int y,int z)
{
    ver[++tot]=y;
    Next[tot]=head[x];
    head[x]=tot;
    edge[tot]=z;
}

Fast read function:

inline int read()
{
    int num=0,w=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch<='9' && ch>='0')
        num=(num<<1)+(num<<3)+ch-'0',ch=getchar();
    return  num*w;
}

Macro definitions:

#define Mi return 
#define manchi 0

Hereinafter except the first example, no solution is described only to FIG.


Select the node class:
No party boss

It is easy to see that this is a selected node class tree dp, because the topics selected for the number of staff and there is no limit.

Then we define dp state based on routines:

定义状态:
f[i][1]表示在以i号节点为根的子树中,i号职员来参加舞会所得的最大快乐指数
f[i][0]则表示i号职员不来参加所得的最大快乐指数

Next we define the parent-child relationship: If your boss an employee to attend the prom, then the staff would in any case would not come to the ball , that father and son can not coexist.

Then as a state transition:

状态转移:
f[i][1]+=max(f[son][0])//由于i去了,所以i的儿子就不能去了,即f[son][0]
f[i][0]+=max(f[son][1],f[son][0])//i没有去,那么i的儿子可去可不去

Consider border:

f[i][1]=happy[i]//初始状态去参加舞会的快乐指数肯定等于自身的快乐指数

A dfs can then put out, \ (QwQ \) .

Code (digraph):

const int N = 6000+5;

int n,happy[N],root;
int head[N],ver[N<<1],tot,Next[N<<1];
int f[N][3];bool vis[N];//vis数组用于找根 
//f[i][0]以i为根,i不去;f[i][1]以i为根,i去
//f[i][0]+=max(f[j][0],f[j][1]),j是i的儿子
//f[i][1]+=max(f[j][0]) 

inline void slove(int x)
{
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        slove(y);
        //dp
        f[x][0]+=max(f[y][0],f[y][1]);
        f[x][1]+=f[y][0];
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        f[i][1]=read();//直接读入的时候初始化 
    for(int i=1;i<=n-1;i++)
    {
        int x=read(),y=read();
        vis[x]=1;add(y,x);
    }int a=read(),b=read();
    
    //找根 
    for(int i=1;i<=n;i++)
        if(!vis[i]){root=i;break;}
    
    slove(root);
    cout<<max(f[root][1],f[root][0]);//输出答案 
    return 0;
}

Code (undirected graph):

inline void dfs(int x,int fa)
{
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        f[x][0]+=max(f[y][0],f[y][1]);
        f[x][1]+=f[y][0];
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        f[i][1]=read();
    for(int i=1;i<=n-1;i++)
    {
        int x=read(),y=read();
        add(y,x);add(x,y);//注意加两次边,转为无向图 
    }int a=read(),b=read();
    dfs(1,0);//任选一个点作为根进行dp,这里选择1作为根. 
    cout<<max(f[1][1],f[1][0]);
    return 0;
}

The largest sub-tree and

Since there is no limit to the number of flowers chosen, which is obviously a kind of tree node selection dp, and then determine the parent-child relationship: father did not choose, the son can not be selected (this is obviously), then go directly routine on the line

const int N = 16000+5;

/*
定义状态:
f[i][1]表示在以i号节点为根的子树中,选择第i号花所得最大美丽指数 
f[i][0]则表示不选择i号花的最大美丽指数

状态转移:
f[i][1]+=max(f[son][0],f[son][1])
f[i][0]=0

边界:
f[i][1]=a[i]
*/ 

int n,f[N][2],root,a[N],ans=-0x7fffffff;
int ver[N<<1],head[N<<1],Next[N<<1],tot;

inline void dfs(int x,int fa)
{
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        f[x][0]=0;
        f[x][1]+=max(f[y][1],f[y][0]);
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++) f[i][1]=read();
    for(int i=1;i<=n-1;i++)
    {
        int x=read(),y=read();
        add(y,x),add(x,y);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
        ans=max(ans,f[i][1]);
    printf("%d",ans);
    Mi manchi;
}

Strategy Game

Apparently a selected node class tree dp, determine paternity: father and son to have a presence (as long as there is one, then this edge between father and son can be overwritten).

Are routine \ (QwQ \)

const int N = 1500+5;
/*
定义状态:
f[i][0]表示以i为根的子树,i结点不放士兵的最小值
f[i][1] 放士兵的最小值

状态转移:
f[i][0]+=f[son][1]//父亲不选择的话,儿子就一定要选择才符合题意 
f[i][1]+=min(f[son][1],f[son][0]);//父亲选择了的话,那么儿子选不选都可以. 

边界:
f[i][1]=1
ans=min(f[i][1],f[i][0])
*/ 
int n,f[N][2];
int tot,head[N],ver[N],Next[N];

inline void dfs(int x,int fa)
{
    f[x][1]=1;
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        f[x][1]+=min(f[y][1],f[y][0]);
        f[x][0]+=f[y][1];
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read(),k=read();
        for(int j=1;j<=k;j++)
        {
            int y=read();
            add(x,y);add(y,x);
        }
    }
    dfs(1,-1);//注意是从节点0开始编号的,所以我们选的root的fa不能为0
    printf("%d",min(f[1][0],f[1][1]));
    Mi manchi;
}

Above all emergent topics, then look at the subject a little bit of thinking difficult.


Security guard

We found this problem and that problem like a strategy game, but the game strategy is concerned that side, and this problem is concerned that the point has not been covered , so we have to re-establish paternity, and redefine the state.

父子关系:
自己(i),父亲(fa),儿子(son)中必须要存在一个
定义状态: 
f[i][0]表示i被自己覆盖 的最小花费
f[i][1]表示i被儿子覆盖 的最小花费
f[i][2]表示i被父亲覆盖 的最小花费
状态转移:
1.f[i][0]+=min(f[son][1],f[son][2],f[son][0]) 
2.f[i][1]=f[x][0]+sigma(min (f[son][0],f[son][1]) ) 
3.f[i][2]+=min(f[son][0],f[son][1])
    
我们分析一下上面状态转移的过程:
1.此时情况是节点i处放置警察,那么它的儿子可以选择被自己看守(f[son][0]),也可以选择被儿子看守(f[son][1]),当然也可以选择被父亲看守(f[son][2])
转移2有点小难度,我们先分析转移3
3.此时的情况是节点i被父亲覆盖(即i节点没有放置警察),那i的儿子(son)就只能选择被自己看守(f[son][0]),或者被它的儿子的看守(f[son][1])

Transfer 2 will now be discussed:

At this point the situation is node \ (i \) at not placed police, node \ (i \) guarded by its son, so we obviously want to \ (i \) many sons, select a son (corresponding to the above transfer equation \ (the X-\) ) placed the police, so the current node \ (i \) will be to guard, and then for the rest of his son, and we transfer equation 3 the same operation on it, because the node \ (i \) is not placed police, that \ (i \) son can only choose to be guards his own, or was it the son of the guard.

So we just need to find for node \ (i \) , one of the best son \ (x \) on the line, you can consider enumerate all the sons, but also a mathematical method to optimize, the following reference

Solution to a problem P2458 security guard

For x, there \ (f [i] [1 ] = f [x] [0] + \ Sigma_ {j \ subset son (i), j! = X} {min (f [j] [0] , f [j] [1] )} \)

If x is not optimal, the presence of y satisfies \ (f [x] [0 ] + \ Sigma_ {j \ subset son (i), j! = X} {min (f [j] [0], f [ j] [1])} < f [y] [0] + \ Sigma_ {j \ subset son (i), j! = y} {min (f [j] [0], f [j] [1] )} \)

Merger of similar items, finishing to give \ (f [x] [0 ] -min (f [x] [0], f [x] [1])> f [y] [0] -min (f [y] [ 0], f [y] [ 1]) \)

So for the best x, only need to meet \ (f [x] [0 ] -min (f [x] [0], f [x] [1]) \) is the smallest of all the sons of it.

We may assume that the best \ (x \) initially is 0, then (f [0] [0] \) \ assigned to a maximum transfer

const int N = 1500+5;
int n,f[N<<1][3];
int tot,head[N],ver[N<<1],Next[N<<1];

inline void dfs(int x,int fa)
{
    int special_son=0;
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        f[x][0]+=min(f[y][0],min(f[y][1],f[y][2]));
        f[x][2]+=min(f[y][0],f[y][1]);
        //找最优的x 
        if((f[special_son][0]-min(f[special_son][0],f[special_son][1])) > (f[y][0]-min(f[y][0],f[y][1])))
            special_son=y;
    }
    //找到x之后,我们还需要求出simga(min (f[j][0],f[j][1]) ) 
    f[x][1]=f[special_son][0];
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa || y==special_son) continue;
        f[x][1]+=min(f[y][0],f[y][1]);
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read(),val=read(),k=read();
        f[x][0]=val;//初始化 
        for(int j=1;j<=k;j++)
        {
            int y=read();
            add(x,y);add(y,x);
        }
    }
    f[0][0]=0x3f3f3f3f;dfs(1,-1);//赋初值 
    printf("%d",min(f[1][0],f[1][1]));
    Mi manchi;
}

By the one example we can see that the class tree node selection dp, the second dimension of its status, the current selection is not limited to whether this node, but expand what is the current state of this point is for the meaning of the questions, it is above the second dimension is represented by an example, node status \ (I \) specific circumstances guarded. Let's look at an example.

Tricolor binary tree

This question is relatively simple, can be said node selection dp advanced class practiced hand the title, this time if we also use \ (f [i] [0/1 ] \) to represent the i-node whether dyed green, then, apparently can not solve the problem, so we have to consider expanding the second dimension, this expansion is a good think. Since the maximum value and the minimum worth seeking process is essentially similar, and therefore the following describes only approach the maximum

定义状态:
f[i][0/1/2]表示在以i为根的子树中,染色绿色的最多个数
且i的颜色为 0/1/2 (绿/红/蓝)

Next we determine paternity, and then transferred on the line based on the specific circumstances.

明确父子关系:父亲和儿子状态不能相同,儿子和儿子之间状态也不能相同

状态转移:
1.i为绿色,那么左右儿子只能为红或蓝
f[i][0]=max(f[lson][1]+f[rson][2],f[lson][2]+f[ron][1])+1
2.i为红/蓝 
f[i][1]=max(f[lson][0]+f[rson][2],f[lson][2]+f[rson][0]);
f[i][2]=max(f[lson][1]+f[rson][0],f[lson][0]+f[rson][1]);
初始f[i][0]=1,f[i][1/2]=0;
ans=max(f[1][0/1/2]) 

The title for the tree structure is strictly limited, which can only have a son, or two sons, then we can use an array to simulate the tree, not edging achievements connected with the collar table.

using namespace std;
const int N = 500000+5;

int f[N][3],g[N][3];
int tot,tree[N][2];
//数组模拟这棵树,tree[i][0/1]表示以i为根的树中,左/右儿子的编号 
char TREE[N];

inline void build(int root)//递归建树 
{
    tot++;
    if(TREE[root]=='0')  return;
    else if(TREE[root]=='1') {tree[root][0]=tot+1;build(tot+1);return;}
    else if(TREE[root]=='2') {tree[root][0]=tot+1;build(tot+1);tree[root][1]=tot+1;build(tot+1);return;}
}

inline void dfs(int fa)
{
    f[fa][0]=g[fa][0]=1;
    if(tree[fa][0] && tree[fa][1])//左右儿子都有
    {
        int l=tree[fa][0],r=tree[fa][1];
        dfs(l);dfs(r);
        f[fa][0]=max(f[l][1]+f[r][2],f[l][2]+f[r][1])+1;
        f[fa][1]=max(f[l][0]+f[r][2],f[l][2]+f[r][0]);
        f[fa][2]=max(f[l][1]+f[r][0],f[l][0]+f[r][1]);
        g[fa][0]=min(g[l][1]+g[r][2],g[l][2]+g[r][1])+1;
        g[fa][1]=min(g[l][0]+g[r][2],g[l][2]+g[r][0]);
        g[fa][2]=min(g[l][1]+g[r][0],g[l][0]+g[r][1]);      
    }
    else if(tree[fa][0] && !tree[fa][1]) 
    {
        int son=tree[fa][0];
        dfs(son);
        f[fa][0]=max(f[son][1],f[son][2])+1;
        f[fa][1]=max(f[son][0],f[son][2]);
        f[fa][2]=max(f[son][0],f[son][1]);
        g[fa][0]=min(g[son][1],g[son][2])+1;
        g[fa][1]=min(g[son][0],g[son][2]);
        g[fa][2]=min(g[son][0],g[son][1]);
    }
}

int main()
{
    scanf("%s",TREE+1);build(1);
    dfs(1);
    printf("%d ",max(f[1][0],max(f[1][1],f[1][2])));
    printf("%d",min(g[1][0],min(g[1][1],g[1][2])));
    Mi manchi;
}

Leaves staining

Not very humanityTranslation that Italy: Given a tree, the nodes in the tree asked to select black or white colored, so as to contain at least one colored simple path from the root node to the leaf node, and gives limiting \ (c [i] \) , from the root to the requirements (I \) \ simple path number leaf node, the last node is a color of the colored \ (C [I] \) , then needs to select the least colored nodes.

In essence remains the selected node class tree dp, its status is very similar to the previous one example, where the first gives the whole idea.

/*
定义状态: 
f[i][0/1/2]表示在以i为根的子树中,i的颜色为(黑色/白色/不染色)所需染色的最小值  
确定父子关系:
保证根结点到每个叶子的简单路径上都至少包含一个有色结点
状态转移:
贪心地去想,保证简单路径上都包含一个有色节点,显然是将这个有色节点放得越高越好,
即让经过这个有色节点的简单路径数增多. 
考虑c[i]这个限制条件,我们只要固定第i个节点的颜色就可以了
1.如果i节点染成黑色,那我们的子节点就不用染成黑色。
由于是最少的着色节点,所以我们父亲的值显然为sigma(son),所以用f[i][]+=
f[i][0]+=min(f[son][0]-1,f[son][1],f[son][2])
2.如果i节点染成白色 
f[i][1]+=min(f[son][0],f[son][1]-1,f[son][2])
3.如果当前节点不染色
不染色的实质是因为无法确定当前节点要染黑色还是白色,还要看fa的情况 
f[i][2]+=min(f[son][0/1/2]) 
边界:f[i][0]=f[i][1]=f[i][2]=1;   f[i][2]的实质是染了一个不确定的颜色,所以其初始值也为1
f[i][!(c[i])]=INF;即i号节点绝对不能染成与c[i]相反的颜色,这点很显然
答案:ans=min(f[i][0/1/2]) 
*/

Greedy strategy:

If the son nodes of a node requires multiple dyed black than white, it is to this node marked black dye, the dye to be colored white son white;

For example node \ (i \) I had three sons, two sons needs dyed black to qualify, you need another dyed white, then obviously we should node \ (i \) dyed black, so it's two son do not need staining, and only need another son dyed white can be.

Son node needs dyed white than black and more empathy.

That there is a situation that is dyed black and white to number as many, then we will consider the first non-staining, look at \ (i \) case of brothers more specific staining, such as node \ (i, j \) are brothers, their father \ (FA \) , \ (i \) node temporarily no staining (can be dyed any color), \ (j \) node needs dyed black, then obviously we should put \ ( i \) node as black, then \ (fa \) will need to be dyed black, \ (i, J \) do not stain, this is clearly the best.

Policy Reference above (recommended reading):

Leaves staining greedy practices

const int N = 100500;
const int INF = 0x3f3f3f3f;

int n,m,tot,c[N],f[N][3];
int ver[N<<1],head[N],Next[N<<1];

inline void dfs(int x,int fa)
{
    if(x<=m) return ;
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        f[x][0]+=min(f[y][0]-1,min(f[y][2],f[y][1]));
        f[x][1]+=min(f[y][0],min(f[y][2],f[y][1]-1));
        f[x][2]+=min(f[y][0],min(f[y][1],f[y][2]));
    }
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++) c[i]=read();
    for(int x,y,i=1;i<n;i++)
    {
        x=read(),y=read();
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
    {
        f[i][0]=f[i][1]=f[i][2]=1;
        if(i<=m) f[i][!(c[i])]=INF;
    }
    dfs(m+1,-1);//以一个不是叶子节点的根 dfs 
    printf("%d",min(f[m+1][1],min(f[m+1][0],f[m+1][2])));
    Mi manchi;
}

Guess you like

Origin www.cnblogs.com/fengzi8615/p/11768259.html