A Simple Start to Tree DP

Posted by anne on Tue, 05 May 2020 06:53:52 +0200

A required topic to get started

Anniversary Part Title Link

thinking

The relationship inside is a numeric acyclic graph, and there are obviously two choices for each node: to go to a party, not to go to a party, so the state transfer equation comes out

  • To queue\(dp[i][1] = dp[i][1] + dp[i_{son}][0]\), the boss attended the party, and his direct reports could not attend the party
  • Not attending a party\(dp[i][0] = dp[i][0] + max(dp[i_{son}][1], dp[i_{son}][0])\), the boss is not attending the party, and his immediate subordinates may or may not attend.

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;

int head[N], to[N], nex[N], in[N], cnt = 1;

int dp[N][2], n;

void add(int x, int y) {
    to[cnt] = y;
    nex[cnt] = head[x];
    head[x] = cnt++;
    in[y]++;
}

void dfs(int rt, int fa) {
    for(int i = head[rt]; i; i = nex[i]) {
        if(to[i] != fa) {
            dfs(to[i], rt);
            dp[rt][1] += dp[to[i]][0];
            dp[rt][0] += max(dp[to[i]][0], dp[to[i]][1]);
        }
    }
}
int main() {
    // freopen("in.txt", "r", stdin);
    while(scanf("%d", &n) != EOF) {
        cnt = 1;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &dp[i][1]);
            head[i] = 0;
            dp[i][0] = 0;
        }
        int x, y;
        while(scanf("%d %d", &x, &y) && (x + y))
            add(y, x);
        for(int i = 1; i <= n; i++)
            if(!in[i]) {
                dfs(i, 0);
                printf("%d\n", max(dp[i][0], dp[i][1]));
            }
    }
    return 0;
}

One idea was right, but the details beat me up.

Rebuilding Roads Title Link

thinking

It's not difficult to think about it. An array of dp[i][j], the path that needs to be cut when the number of nodes connected to the ith node on the record plus the number of nodes connected to it on its subtree is obviously dp[i][1] =the subtree directly connected to it plus the path of a parent node.

Then the state transfer equation has \(dp[i][j] = min (dp[i][j], dp[i][k] + dp[i_{son}][j-k] - 2)\, which is subtracted by 2 because the two connected edges are recorded in the DP array of each other.

Now let's go into one detail. In our final answer, we subtract 1 from all of dp[root][] because we added the edges of its parent node before, but he has no parent node.It's a bit of a pothole, or a delicious dish.

Code

// #include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 2e2 + 10;

int head[N], to[N], nex[N], in[N], cnt;

int dp[N][N], sz[N], n, m;

void add(int x, int y) {
    to[cnt] = y;
    nex[cnt] = head[x];
    head[x] = cnt++;
    in[y]++;
}
void dfs1(int rt, int fa) {//Find the number of directly connected nodes and initialize the dp array.
    sz[rt] = 1;
    for(int i = head[rt]; i; i = nex[i]) {
        if(to[i] != fa) {
            dfs1(to[i], rt);
            sz[rt] ++;
        }
    }
}

void solve(int rt, int fa) {
    for(int i = head[rt]; i ; i = nex[i]) {
        if(to[i] != fa) {
            solve(to[i], rt);
            for (int j = m; j > 1; j--)
            for (int k = 1; k < j; k++)
                dp[rt][j] = min(dp[rt][j], dp[rt][j - k] + dp[to[i]][k] - 2);
        }
    }
}
int main() {
    // freopen("in.txt", "r", stdin);
    int x, y;
    while(scanf("%d %d", &n, &m) != EOF) {
        memset(head, 0, sizeof head);
        memset(dp, 0x3f, sizeof dp);
        cnt = 1;
        for(int i = 1; i < n; i++) {
            scanf("%d %d", &x, &y);
            add(x, y);
        }
        for(int i = 1; i <= n; i++)
            if(in[i] == 0) {
                // cout << i << endl;
                dfs1(i, 0);
            }
        for(int i = 1; i <= n; i++)//It seems that this can be done directly in dfs1,,
            dp[i][1] = sz[i];
            // cout << sz[i] << endl;
        for(int i = 1; i <= n; i++)
            if(in[i] == 0) {
                solve(i, 0);
                dp[i][m]--;
            }
        // For(int I = 1; I <= n; i++)//debug bug_ing
        //     for(int j = 1; j <= m; j++) {
        //         printf("%d%c", dp[i][j], j == m ? '\n' : ' ');
        //     }
        int ans = 0x3f3f3f3f;
        for(int i = 1; i <= n; i++)
            dp[i][m] < ans ? ans = dp[i][m] : ans = ans;//Finally, I got the answer. I wrote a long list of expressions that I don't understand right now,,
        printf("%d\n", ans);
    }
    return 0;
}

A slightly more complex topic

Actually, it's quite understandable.

Optimal Connected Subset Title Links

thinking

This is a question of building your own map. Obviously, it's not difficult to imagine that if you build a connected edge between two points of dis = 1, the next thing to do is DP s on the tree.

Considering the state transfer equation, for each point we can put it in the union set or not, and at the same time we must ensure that each pair of points is connected to each other.

We use dp[i][0] to indicate that this point is not on the union set, and dp[i][1] to indicate that this point is on the union set.

Considering how dp[i][0] changes, it is not difficult to see that its value must be the maximum value on its subtree, so \(dp[i][0] = max(dp[i][0], dp[i_{son}][1], dp[i_{son}][0])\

Next we consider dp[i][1], and we want to ensure its connectivity by getting \(dp[i][1] = max(dp[i][1], dp[i_{son}][1] + dp[i][0])

Then follow this idea and run DFS once.

Code

// #include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N1 = 1e3 + 10, N2 = 1e6 + 10;

int head[N1], to[N2], nex[N2], cnt;

int dp[N1][2], n;

struct point {
    int x, y;
}a[N1];

void add(int x, int y) {
    to[cnt] = y;
    nex[cnt] = head[x];
    head[x] = cnt++;
}

void dfs(int rt, int fa) {
    for(int i = head[rt]; i; i = nex[i]) {
        if(to[i] != fa) {
            // cout << 1 << endl;
            dfs(to[i], rt);
            dp[rt][1] = max(dp[rt][1], dp[rt][1] + dp[to[i]][1]);
            dp[rt][0] = max(dp[rt][0], max(dp[to[i]][0], dp[to[i]][1]));
        }
    }

}

int main() {
    // freopen("in.txt", "r", stdin);
    int x, y, w;
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i++)
            dp[i][0] = dp[i][1] = head[i] = 0;
        cnt = 1;
        for(int i = 1; i <= n; i++) {
            scanf("%d %d %d", &a[i].x, &a[i].y, &w);
            dp[i][1] = w;
        }
        for(int i = 1; i <= n; i++)
            for(int j = i + 1; j <= n; j++)
                if((abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y)) == 1) {
                    add(i, j), add(j, i);
                    // cout << 1 << endl;
                }
        dfs(1, 0);
        // for(int i = 1; i <= n; i++)
        //     printf("%d %d\n", dp[i][0], dp[i][1]);
        printf("%d\n", max(dp[1][0], dp[1][1]));
    }
    return 0;
}

There should have been a fourth question, and I didn't expect to look at the tree backpack, but I haven't learned it yet, so I started by croaking.