zoukankan      html  css  js  c++  java
  • HZNUACM寒假集训Day10小结 树树形DP

      树形DP

      加分二叉树 洛谷P1040

      注意中序遍历的特点:当根节点编号k时,编号小于k的都在其左子树上,编号大于k的都在右子树

      转移方程 f[i,j]=max{f[i,k-1]*f[k+1,j]+d[k]} ,f[i,j]表示中序遍历i到j的二叉树最大加分  时间复杂度O(N3

      

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    const double PI = acos(-1.0);
    typedef long long ll;
    using namespace std;
    
    const int maxn = 35;
    int n, m;
    int f[maxn][maxn], root[maxn][maxn], num[maxn];
    
    void print(int l, int r) {         //先序遍历  根->左子树->右子树
        printf("%d ", root[l][r]);
        if (root[l][r] > l) print(l, root[l][r] - 1);
        if (root[l][r] < r) print(root[l][r] + 1, r);
    }
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &num[i]);
            f[i][i] = num[i];
            root[i][i] = i;
            f[i][i - 1] = f[i + 1][i] = 1;
        }
        for (int i = n; i >= 1; i--) {
            for (int j = i + 1; j <= n; j++) {
                for (int k = i; k <= j; k++) {
                    if (f[i][k - 1] * f[k + 1][j] + f[k][k] <= f[i][j]) continue;
                    f[i][j] = f[i][k - 1] * f[k + 1][j] + f[k][k];
                    root[i][j] = k;
                }
            }
        }
        printf("%d\n", f[1][n]);
        print(1, n);
        return 0;
    }

        P2015 二叉苹果树

       

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    const double PI = acos(-1.0);
    typedef long long ll;
    using namespace std;
    
    int n, cnt[110], dp[110][110];
    int q;
    struct Edge {
        int w;
        int e;
    }t;
    vector<Edge> e[220];
    void dfs(int u, int p) {
        for (int i = 0; i < e[u].size(); i++) {
            int v = e[u][i].e;
            if (v == p) continue;
            dfs(v, u);
            cnt[u] += cnt[v] + 1;
            for (int j = min(cnt[u], q); j; j--) {
                for (int k = min(j - 1, cnt[v]); k >= 0;)
                    dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[v][k] + e[u][i].w);
            }
        }
    }
    int main() {
        scanf("%d%d", &n, &q);
        for (int i = 1; i < n; i++) {
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            t.e = y;
            t.w = w;
            e[x].push_back(t);
            t.e = x;
            e[y].push_back(t);
        }
        dfs(1, 0);
        printf("%d", dp[1][q]);
        return 0;
    }

        洛谷P1352 没有上司的舞会

       

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    const double PI = acos(-1.0);
    typedef long long ll;
    using namespace std;
    
    const int maxn = 6005;
    int w[maxn];
    int v[maxn];
    int f[maxn][2];
    vector<int> son[maxn];
    
    void dfs(int x) {                  
        f[x][0] = 0;                   //初始化  f[x][0]表示以x为根的子树,且x不参加舞会的最大快乐值
        f[x][1] = w[x];                //f[x][1]表示以x为根的子树,且x参加舞会的最大快乐值
        for (int i = 0; i < son[x].size(); i++) {
            int y = son[x][i];
            dfs(y);
            f[x][0] += max(f[y][0], f[y][1]);    //状态转移方程
            f[x][1] += f[y][0];                  
        }
    }
    
    int main() {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
        for (int i = 1; i <= n - 1; i++) {
            int x, y;
            scanf("%d%d", &x, &y);    //注意父亲在后
            son[y].push_back(x);
            v[x]++;
        }
        int root;
        for (int i = 1; i <= n; i++) {
            if (!v[i]) {
                root = i;  break;
            }
        }
        dfs(root);
        printf("%d\n", max(f[root][0], f[root][1]));
        return 0;
    }

       树上的常见操作

       

    const int maxn = 100005;
    vector<int> v[maxn];
    int _size[maxn];    //结点大小
    int mx_size[maxn];
    int depth[maxn];
    int Max[maxn];
    int val[maxn];
    
    void getsize(int x) {               //一棵N个点的无权树,问每个结点的大小
        _size[x] = 1;  
        for (int i = 0; i < v[x].size(); i++) {
            getsize(v[x][i]);
            _size[x] += _size[v[x][i]];
        }
    }
    
    void getdep(int x) {                //一棵N个点的无权树,问每个结点的深度
        for (int i = 0; i < v[x].size(); i++) {    
            depth[v[x][i]] = depth[x] + 1;
            getdep(v[x][i]);           //自顶向下
        }
    }
    
    void getmax(int x) {             //一棵N个点的点权树,问每个子树的点权和,点权最大值
        Max[x] = val[x];
        for (int i = 0; i < v[x].size(); i++) {
            getmax(v[x][i]);
            Max[x] = max(Max[x], Max[v[x][i]]);
        }
    }

       求树的重心

       定义:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡

       

      C++代码

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<cmath>
    const double PI = acos(-1.0);
    #define INF 0x3f3f3f3f
    typedef long long ll;
    using namespace std;
    
    const int maxn = 105;
    int n, m, ans;
    int len, lenp;
    int _size[maxn], id[maxn], p[maxn];
    vector<int> e[maxn];
    bool vis[maxn];
    
    int dfs(int x) {
        if (!e[x].size()) return 1;
        int sum = 1;
        for (int i = 0; i < e[x].size(); i++) {
            if (!vis[e[x][i]]) {
                vis[e[x][i]] = true;
                sum += dfs(e[x][i]);
            }
        }
        return sum;
    }
    
    int main() {
        scanf("%d", &n);
        int x, y;
        for (int i = 1; i < n; i++) {
            scanf("%d%d", &x, &y);
            e[x].push_back(y);
        }
        for (int i = 1; i <= n; i++) {
            memset(vis, 0, sizeof vis);
            vis[i] = 1;
            _size[i] = dfs(i);
        }
        int Min = INF;
        for (int i = 1; i <= n; i++) {
            int Max = n - _size[i];
            for (int j = 0; j < e[i].size(); j++) Max = max(Max, _size[e[i][j]]);
            Min = min(Max, Min);
            p[i] = Max;
        }
        for (int i = 1; i <= n; i++) {
            if (p[i] == Min) {
                lenp++;
                id[lenp] = i;
            }
        }
        printf("%d\n", lenp);             //重心个数
        for (int i = 1; i <= lenp; i++) printf("%d\n", id[i]);
        return 0;
    }
  • 相关阅读:
    服务器迁移总结
    使用OpenSSL生成证书
    mysql Event、存储过程、表命令
    负载均衡 > 常见问题
    SpringMVC记住密码功能
    spring mvc +cookie+拦截器功能 实现系统自动登陆
    android studio之argument for @notnull parameter 'name'
    jQuery ajax表单提交实现局部刷新
    Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法
    ajax表单提交全路径
  • 原文地址:https://www.cnblogs.com/hznumqf/p/12272614.html
Copyright © 2011-2022 走看看