• 洛谷 P2015 二叉苹果树 && caioj1107 树形动态规划(TreeDP)2:二叉苹果树


    这道题一开始是按照caioj上面的方法写的
    (1)存储二叉树用结构体,记录左儿子和右儿子
    (2)把边上的权值转化到点上,离根远的点上
    (3)用记忆化搜索,枚举左右节点分别有多少个点,去递归

    这种写法有个好处, 避免了总的树枝个数的枚举

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 112;
    struct node 
    {
     	int v, w; 
     	node(int v = 0, int w = 0) : v(v), w(w) {}
    };
    struct tree
    {
    	int l, r;
    	tree() { l = r = 0; }
    }a[MAXN];
    vector<node> g[MAXN];
    int f[MAXN][MAXN], n, q;
    
    void dfs(int x, int fa)
    {
    	REP(i, 0, g[x].size())
    	{
    		int v = g[x][i].v, w = g[x][i].w;
    		if(v == fa) continue;
    		f[v][1] = w;
    		if(!a[x].l) a[x].l = v;
    		else a[x].r = v;
    		dfs(v, x);
    	}
    }
    
    int tree_dp(int x, int k)
    {
    	if(x == 0) return 0;
    	if(f[x][k] != -1) return f[x][k];
    	
    	int maxt = 0;
    	REP(i, 0, k)
    	{
    		int ls = i, rs = k - i - 1;
    		maxt = max(maxt, f[x][1] + tree_dp(a[x].l, ls) + tree_dp(a[x].r, rs));
    	}
    	return f[x][k] = maxt;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &q);
    	REP(i, 0, n - 1)
    	{
    		int u, v, w;
    		scanf("%d%d%d", &u, &v, &w);
    		g[u].push_back(node(v,w));
    		g[v].push_back(node(u,w));
    	}
    	
    	memset(f, -1, sizeof(f));
    	dfs(1, -1);
    	REP(i, 1, n + 1) f[i][0] = 0;
    	f[1][1] = 0;
    	printf("%d
    ", tree_dp(1, q + 1));
    	
    	return 0;
    }

    然后看到洛谷上还有更加简洁的写法
    先往下搜索,然后回溯的时候记录边的数量,然后枚举左右节点
    取多少树枝,取max。
    f[u][j] = max(f[u][j-k-1] + f[v][k] + w);

    然后这里用到了01背包的思想

    树枝可以看成从的总的重量,所以要逆序

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 112;
    struct node 
    {
     	int v, w; 
     	node(int v = 0, int w = 0) : v(v), w(w) {}
    };
    vector<node> g[MAXN];
    int f[MAXN][MAXN], b[MAXN], n, q;
    
    void dfs(int u, int fa)
    {
        REP(i, 0, g[u].size())
        {
            int v = g[u][i].v, w =  g[u][i].w;
            if(v == fa) continue;
            dfs(v, u);
            b[u] += b[v] + 1;
            for(int j = min(q, b[u]); j >= 0; j--)
            	for(int k = 0; k <= min(b[v], j - 1); k++)
                    f[u][j] = max(f[u][j], f[u][j-k-1] + f[v][k] + w);
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &q);
        REP(i, 1, n)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            g[u].push_back(node(v,w));
            g[v].push_back(node(u,w));
        }
        
        dfs(1, 0);
        printf("%d
    ", f[1][q]);
        
        return 0;
    }

    做完了选课在来看这一题,又有新的感悟。

    树上背包的做法适用于多叉树和二叉树,二叉树是两个物品,多叉树是多个物品。

    而这道题有一点不同是权值是边。那么吸取上面写的经验。

    我们可以把边的权值转移到离根远的点上,同时可以取的点数为边数+1

    然后就一样啦!!

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 112;
    int f[MAXN][MAXN], n, q;
    struct node
    {
    	int v, w;
    	node(int v = 0, int w = 0) : v(v), w(w) {}
    };
    vector<node> g[MAXN];
    
    int dfs(int u, int fa)
    {
    	int sum = 1;
    	REP(i, 0, g[u].size())
    	{
    		int v = g[u][i].v, w = g[u][i].w;
    		if(v == fa) continue;
    		f[v][1] = w;
    		int t = dfs(v, u);
    		sum += t;
    		
    		for(int j = sum; j >= 1; j--)
    			for(int k = 0; k <= min(t, j - 1); k++)
    				f[u][j] = max(f[u][j], f[u][j-k] + f[v][k]);
    	}
    	return sum;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &q);
    	REP(i, 0, n - 1)
    	{
    		int u, v, w;
    		scanf("%d%d%d", &u, &v, &w);
    		g[u].push_back(node(v, w));
    		g[v].push_back(node(u, w));
    	}
    	dfs(1, -1);
    	printf("%d
    ", f[1][q+1]);
    	return 0;
    }
  • 相关阅读:
    动图+源码,演示 Java 中常用数据结构执行过程及原理
    16 个超级实用的 Java 工具类
    图解 Java 垃圾回收机制,写得非常好!
    一些值得收藏的开源框架
    JVM 发生内存溢出的 8 种原因、及解决办法
    VC的function类说明 -- 继续
    引用文章 如何在lambda中引入递归调用
    VC中function函数解析
    folly无锁队列正确性说明
    C++ Programming Language中的Calculator源代码
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819398.html
Copyright © 2020-2023  润新知