zoukankan      html  css  js  c++  java
  • 洛谷 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;
    }
  • 相关阅读:
    企业应用开发中最常用c++库
    ESOURCE_LOCKED
    c++标准之于gcc/vc/boost等实现相当于jsr规范之于sunjdk/ibmjdk/tomcat/weblogic等实现
    c++的各种类型转换方式
    c++的友元类、方法及其益处
    c/c++的typedef/using类型别名
    c++的class声明及相比java的更合理之处
    c++ sleep(windows/linux)
    c++不自动生成相关函数比如赋值、拷贝函数
    c++继承、多态以及与java的行为差异之处
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819398.html
Copyright © 2011-2022 走看看