DP study notes tree

DP study notes tree

ps: this article is consistent with the Blue Book

The center of gravity of the tree

  • Concept: a node tree minimum of its largest subtree node tree
  • Solution: to find his son with each node \ (size \) , the number of nodes above the sub-tree is \ (the n--size_u \) , find the maximum value for each child node of the tree, find the minimum that would All right;

(I think you do not need a code)


The diameter of the tree

  • The concept: a weighted longest path tree
  • Solution: maintaining a maximum distance of the node to the leaf node \ (d1 [i] \) and the second largest distances \ (D2 [I] \) , the maximum distance is $ max {d1 [i] + d2 [i]} $

code

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e4+5;
int n;
struct pp
{
    int to,next;
}w[2*N];
int head[N],cnt;
int d1[N],d2[N];
int ans;
void add(int x,int y)
{
    cnt++;
    w[cnt].next=head[x];
    w[cnt].to=y;
    head[x]=cnt;
}
void dfs(int x,int fa)
{
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(t!=fa)
        {
            dfs(t,x);
            if(d1[t]+1>d1[x])
            {
                d2[x]=d1[x];
                d1[x]=d1[t]+1;
            }
            else if(d1[t]+1>d2[x]) d2[x]=d1[t]+1;
        }
    }
    return ;
}
void find_ans(int x,int fa)
{
    ans=max(ans,d1[x]+d2[x]);
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(t!=fa) find_ans(t,x);
    }
    return;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("diam.in","r",stdin);
    freopen("diam.out","w",stdout);
#endif
    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(1,0);
    find_ans(1,0);
    printf("%d",ans);
    return 0;
}

example

P4480 truant children

  • Thinking about: the diameter of the tree and obtaining left and right end points, and then set \ (d [i] \) is the node tree \ (I \) to the left end of the smaller distance, and then obtains \ (max \ {d [I] \} \) , then this value plus the diameter is \ (ANS \) ;

code

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=2e5+5;
struct pp
{
    int next,to;
    ll qu;
}w[N*2];
int head[N],cnt;
int n,m;
bool v[N];
ll d1[N],d2[N],dl[N],dr[N];
int f1[N],f2[N];
int r,l;
ll ans,mans;
void add(int x,int y,int z)
{
    w[++cnt].next=head[x];
    w[cnt].qu=z;
    w[cnt].to=y;
    head[x]=cnt;
}
int read()
{
    int f=1;
    char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    int res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
    return res*f;
}
void dfs1(int x)
{
    if(v[x]) return ;
    v[x]=1;
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(!v[t])
        {
            dfs1(t);
            if(d1[t]+w[i].qu>d1[x])
            {
                f2[x]=f1[x];
                f1[x]=f1[t];
                d2[x]=d1[x];
                d1[x]=d1[t]+w[i].qu;
            }
            else if(d1[t]+w[i].qu>d2[x]) d2[x]=d1[t]+w[i].qu,f2[x]=f1[t];
        }
        
    }
    return;
}
void find_ans(int x)
{
    if(v[x]) return;
    v[x]=1;
    if(ans<d1[x]+d2[x])
    {
        ans=d1[x]+d2[x];
        l=f1[x];
        r=f2[x];
    }
    for(int i=head[x];i;i=w[i].next) find_ans(w[i].to);
}
void dfs2(int x)
{
    if(v[x]) return;
    v[x]=1;
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(!v[t])
        {
            dl[t]=dl[x]+w[i].qu;
            dfs2(t);
        }
    }
    return;
}
void dfs3(int x)
{
    if(v[x])return;
    v[x]=1;
    
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(!v[t])
        {
            dr[t]=dr[x]+w[i].qu;
            dfs3(t);
        }
    }
    return;
}
void dfs_ans(int x)
{
    if(v[x]) return;
    v[x]=1;
    mans=max(mans,min(dl[x],dr[x]));
    for(int i=head[x];i;i=w[i].next) dfs_ans(w[i].to);
    return;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("Chris.in","r",stdin);
    freopen("Chris.out","w",stdout);
#endif
    n=read();
    m=read();
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        x=read(),y=read(),z=read();
        add(x,y,z);
        add(y,x,z);
    }
    for(int i=1;i<=n;i++) f1[i]=i;
    dfs1(1);
    memset(v,0,sizeof(v));
    find_ans(1);
    memset(v,0,sizeof(v));
    dfs2(l);
    memset(v,0,sizeof(v));
    dfs3(r);
    memset(v,0,sizeof(v));
    dfs_ans(1);
    printf("%lld",ans+mans);
    return 0;
}

Center tree

  • Concept: given a weighted tree, seeking a node, this node to the tree so that the maximum distance of a minimum of other nodes;

  • Solution: If it is not a negative edge weight of the tree, then straight to the midpoint of the diameter of the fine;

    But here we consider the case of a negative edge weights:

    There are two cases:

    1. From (U \) \ longest path points upward, to \ (up [U] \) ;
    2. From (U \) \ point downwards, i.e. \ (U \) to the most distant leaf node, is set to \ (D1 [U] \) (Ci far to \ (D2 [U] \) );

    \ (d1 [u] \) and \ (d2 [u] \) will ask the question is \ (up [u] \) how to find?

    Or classification of discussion, let \ (u \) father is \ (the X-\) , \ (d1 [the X-] \) from the sub-node \ (v \) ; and that for \ (u \) :

    1. If \ (! U = V \) , then the \ (up [U] = max \ {D1 [X], up [X] \} + DIS [X] [T] \) ;
    2. If \ (U == V \) , then the \ (up [U] = max \ {D2 [X], up [X] \} + DIS [X] [T] \) , which is why to maintain \ ( d2 [x] \) of the reasons;

code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
struct pp
{
    int next,to;
}w[2*N];
int n,k;
int head[N],cnt;
int d1[N],d2[N],pre[N],u[N];
int root,far;
int read()
{
    int f=1;
    char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    int res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
    return res*f;
}
void add(int x,int y)
{
    cnt++;
    w[cnt].next=head[x];
    w[cnt].to=y;
    head[x]=cnt;
}
bool cmp(int x,int y) {return x>y;}
void dfs1(int x,int fa)
{
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(t!=fa)
        {
            dfs1(t,x);
            if(d1[t]+1>d1[x])
            {
                pre[x]=t;
                d2[x]=d1[x];
                d1[x]=d1[t]+1;
            }
            else if(d1[t]+1>d2[x]) d2[x]=d1[t]+1;
        }
    }
    return;
}
void dfs2(int x,int fa)
{
    int minx=min(u[x],d1[x]);
    if(far<minx)
    {
        root=x;
        far=minx;
    }
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if (t!=fa)
        {
            if(pre[x]!=t) u[t]=max(d1[x],u[x])+1;
            else u[t]=max(d2[x],u[x])+1;
            dfs2(t,x);
        }
    }
    return ;
}
int main()
{
    n=read(),k=read();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read(),y=read();
        add(x,y);
        add(y,x);
    }
    dfs1(1,0);
    dfs2(1,0);
    printf("%d",root);
    return 0;
}

example

P5536 core city

  • Ideas: Obviously there will be a city which is the center of the old tree; to find out that this center, the Fengyun tree without roots it goes to the root of rooted trees; then find each node except the root node the maximum depth can reach \ (deepfar [i] \) , which is the farthest from the node can reach; then \ (sort \) about \ (deepfar [] \) , the answer is \ (deepfar [k + 1 ] +1 \) ;

code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
struct pp
{
    int next,to;
}w[2*N];
int n,k;
int head[N],cnt;
int d1[N],d2[N],pre[N],u[N];
int fardeep[N];
int root,far;
int read()
{
    int f=1;
    char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    int res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
    return res*f;
}
void add(int x,int y)
{
    cnt++;
    w[cnt].next=head[x];
    w[cnt].to=y;
    head[x]=cnt;
}
bool cmp(int x,int y) {return x>y;}
void dfs1(int x,int fa)
{
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(t!=fa)
        {
            dfs1(t,x);
            if(d1[t]+1>d1[x])
            {
                pre[x]=t;
                d2[x]=d1[x];
                d1[x]=d1[t]+1;
            }
            else if(d1[t]+1>d2[x]) d2[x]=d1[t]+1;
        }
    }
    return;
}
void dfs2(int x,int fa)
{
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if (t!=fa)
        {
            if(pre[x]!=t) u[t]=max(d1[x],u[x])+1;
            else u[t]=max(d2[x],u[x])+1;
            dfs2(t,x);
        }
    }
    return ;
}
void dfs3(int x,int fa)
{
    int minx=min(u[x],d1[x]);
    if(far<minx)
    {
        root=x;
        far=minx;
    }
    for(int i=head[x];i;i=w[i].next) if(w[i].to!=fa) dfs3(w[i].to,x);
    return;
}
void dfs4(int x,int fa)
{
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(t!=fa)
        {
            dfs4(w[i].to,x);
            fardeep[x]=max(fardeep[x],fardeep[t]+1);
        }
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("XR-3.in","r",stdin);
    freopen("XR-3.out","w",stdout);
#endif
    n=read(),k=read();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read(),y=read();
        add(x,y);
        add(y,x);
    }
    dfs1(1,0);
    dfs2(1,0);
    dfs3(1,0);
    dfs4(root,0);
    sort(fardeep+1,fardeep+1+n,cmp);
    printf("%d",fardeep[k+1]+1);
    return 0;
}

The above are some of the classic questions about the tree, the following is today's hero - tree DP


Backpack tree DP

(I think the fact the left and right sub-tree DP class tree may fall into this category)

example

Course

Is the time complexity of the book \ (n ^ 3 \) algorithm, an optimization described here, which can be reduced to say \ (n-2 ^ \) ;

Before no optimization, DP equation:
\ [DP [U] [J] = max \ {DP [U] [J], DP [U] [JK-. 1] + DP [V] [K] \} + V [U] \]
Thus for each node must be \ (n ^ 2 \) violent enumeration \ (J \) and \ (K \) ;

Optimized, we DP becomes the equation:
\ [\ Cases} {the begin DP [V] [J] DP = [U] [J] + V [V] \\ DP [U] [J] = max \ {dp [u] [j]
, dp [v] [j-1] \} \ end {cases} \] it is another generalization of items of optimization, the basic tree backpack DP equation; we will only need to \ ( O (n) \) enumeration \ (J \) like;

code

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

int n,m;
struct edge
{
    int next,to;
}e[1000];
int rt,head[1000],tot,val[1000],dp[1000][1000];
void add(int x,int y)
{
    e[++tot].next=head[x];
    head[x]=tot;
    e[tot].to=y;
}
void dfs(int u,int t)
{
    if (t<=0) return ;
    for (int i=head[u]; i; i=e[i].next)
    {
        int v = e[i].to;
        for (int j=0; j<t; ++j) //这里j从o开始到tot-1,因为v的子树可以选择的节点是u的子树的节点数减一;
            dp[v][j] = dp[u][j]+val[v];
        dfs(v,t-1);
        for (int j=1; j<=t; ++j) 
            dp[u][j] = max(dp[u][j],dp[v][j-1]);//u必须选,所以u选择j个点v只能选择j-1个点;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int a;
        scanf("%d%d",&a,&val[i]);
        if(a)
          add(a,i);
        if(!a)add(0,i);
    }
    dfs(0,m);
    printf("%d",dp[0][m]);
}

Select category tree DP

DP basic equation:

\[ v\in{son(u)} \begin{cases} f[u][0]=\sum f[v][1] \\ f[u][1]=min\{f[v][1],f[v][0]\}+1 \end{cases} \]

example

P2016 strategy game

DP equation directly set like;

code

#include<iostream>
#include<cstdio>
using namespace std;
int n;
int dp[1605][2];
struct pp
{
    int next,to;
}w[1600<<1];
int head[1600],cnt;
void add(int x,int y)
{
    cnt++;
    w[cnt].to=y;
    w[cnt].next=head[x];
    head[x]=cnt;
}
void dfs(int x,int fa)
{
    dp[x][1]=1;
    for(int i=head[x];i;i=w[i].next)
    {
        int t=w[i].to;
        if(t==fa) continue;
        dfs(t,x);
        dp[x][0]+=dp[t][1];
        dp[x][1]+=min(dp[t][0],dp[t][1]);
    }
    return;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a,k;
        scanf("%d%d",&a,&k);
        for(int i=1;i<=k;i++)
        {
            int b;
            scanf("%d",&b);
            add(a,b);
            add(b,a);
        }
    }
    dfs(0,0);
    printf("%d",min(dp[0][1],dp[0][0]));
    return 0;
}

DP ordinary tree

This tree DP is more flexible, just like before there are two kinds of substantially fixed equation DP, or direct it to a few examples;(funny

example

LOJ # 10157. Palace guards

At first glance the title, ahem, select the template tree DP, happy to play codes, congratulations 0;

A closer look at this question in fact is not no party boss, but a cover DP question, the difference between what it?

This question is a side ends have at least one point, there may be two, but no boss is my ball ends up one side there is a point, can not;

Well, in this case a node u minimum funding can not be as simple as selecting DP election by the election of his son not transferred over, because they had not conflict, but must be covered (where coverage radius of each node 1), so that minimum requirements for a node u can be transferred from the node covering it over, so you need to consider three cases:

First set \ (dp [u] [0 ] \) represents nodes \ (U \) is covered by the father and \ (U \) not selected, \ (DP [U] [. 1] \) represents own child nodes and covering \ (U \) not selected, \ (DP [U] [2] \) represents covered itself;

So there are state transition equation:

  • For \ (DP [U] [0] \) , since \ (U \) is not selected, so that for \ (U \) child node \ (V \) , either \ (son (v) \) covered , either \ (v \) covering yourself:

\[ dp[u][0]=\sum min\{dp[v][1],dp[v][2]\} +a[f[u]]; \]

  • For \ (dp [U] [1] \) , to ensure that \ (u \) must be covered to a child node, but also to ensure \ (u \) child node \ (v \) is not covered by father is covered under the premise, that apparently \ (DP [U] [. 1] \) , by \ (dp [v] [1 ] \) and \ (dp [v] [2 ] \) transferred from, but how to ensure \ (dp [u] [1 ] \) of metastasis must contain \ (dp [v] [2 ] \) it?

    At this time there is a clever way, set parameters:
    \ [D min = \ {D, DP [V] [2] -min \ {DP [V] [. 1], DP [V] [2] \} \ } \]
    \ (D \) initial value \ (0x7FFFFFFF \) ;

    For such \ (dp [u] [1 ] \) there is a state transition equation:
    \ [DP [U] [. 1] = \ min SUM \ {DP [V] [. 1], DP [V] [2] \ } + d \]

  • For \ (DP [U] [2] \) , it is clear that it can be transferred from any of the three status sub-node over, but for \ (DP [V] [0] \) , which has already been refilled again \ (A [ U] \) , while for \ (dp [U] [2] \) , and must be applied only once \ (A [U] \) , how to do it? sentenced by a single unique \ (dp [v] [ 0] \) case transferred from the control \ (a [u] \) just add it again? obviously it is possible, but too much trouble, so additional consideration, where you can see the \ (dp [v] [0 ] \ ) only to \ (dp [u] [2 ] \) transfer on, then according to \ (dp [u] [2 ] \) demand \ (dp [v] [0 ] \) state transition equation of a change change:
    \ [DP [U] [0] = \ SUM min \ {DP [V] [. 1], DP [V] [2] \} \]
    (where \ (U \) for \ (V \ ) it's)

    Emotional understanding of what is, if \ (dp [u] [2 ] \) could not help \ (dp [v] [0 ] \) transferred from it to \ (dp [v] [0 ] \) no use, that the \ (dp [v] [0] \) transferred from that in \ (dp [u] [2] \) this add it again \ (a [u] \) is enough, because \ (dp [u] [2 ] \) has guaranteed the \ (U \) is selected, there is no need \ (dp [v] [0 ] \) and then again guaranteed;

    For such \ (DP [U] [2] \) , there is a state transition equation:
    \ [DP [U] [2] = \ min SUM \ {DP [V] [. 1], DP [V] [2] , dp [v] [0] \} + a [u] \]

To conclude there are three state transition equation:
$$
\ Cases} {the begin
DP [U] [0] = \ {SUM DP min [V] [. 1], DP [V] [2]}; \

dp[u][1]=\sum min{dp[v][1],dp[v][2]}+d ;(d=min{d,dp[v][2]-min{dp[v][1],dp[v][2]}})\

dp[u][2]=\sum min{dp[v][1],dp[v][2],dp[v][0]} +a[u]
\end{cases}
$$
(So, it is clear the state transition equation in the book are wrong)

Not difficult to find, the revised \ (dp [v] [0 ] \) must be less than equal to \ (dp [v] [1] \) ; write code so when I pulled the \ (dp [u] [2 ] \) was transferred into the equation:
\ [DP [U] [2] = \ min SUM \ {DP [V] [2], DP [V] [0] \} + a [U] \]
Although the subject already solved, but I still want to go into it; this equation do you mean?

In my emotional understanding is that \ (v \) Now that would be its father \ (u \) to cover, it may not be necessary to ensure that \ (v \) must be covered by its son, modified \ ( dp [v] [0] \ ) just as it is the case;

(Well, bb so much nonsense, a little on useful things, directly on the bar code)

code

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1500 + 5;
int dp[N][3];
int v[N], n, root;
struct pp {
    int next, to;
} w[N];
int head[N], cnt, du[N];
void add(int x, int y) {
    cnt++;
    w[cnt].next = head[x];
    w[cnt].to = y;
    head[x] = cnt;
}
void dfs(int x) {
    int d = 0x7fffffff;
    for (int i = head[x]; i; i = w[i].next) {
        int t = w[i].to;
        dfs(t);
        dp[x][0] += min(dp[t][1], dp[t][2]);
        dp[x][1] += min(dp[t][1], dp[t][2]);
        d = min(d, dp[t][2] - min(dp[t][1], dp[t][2]));
        dp[x][2] += min(dp[t][2], dp[t][0]);
    }
    dp[x][1] += d;
    dp[x][2] += v[x];
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("guard.in", "r", stdin);
    freopen("guard.out", "w", stdout);
#endif
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        int x, m;
        scanf("%d", &x);
        scanf("%d", &v[x]);
        scanf("%d", &m);
        for (int j = 1; j <= m; j++) {
            int y;
            scanf("%d", &y);
            add(x, y);
            du[y]++;
        }
    }
    for (int i = 1; i <= n; i++)
        if (!du[i])
            root = i;
    dfs(root);
    printf("%d", min(dp[root][1], dp[root][2]));
    return 0;
}

Well, almost over, though a little time consuming to write this, but for me this is konjac deepened understanding of the DP, the harvest is not small, it is not a waste of time, right(escape);

Guess you like

Origin www.cnblogs.com/Wednesday-zfz/p/12209729.html