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;
    }
  • 相关阅读:
    hdu 4027 Can you answer these queries? 线段树
    ZOJ1610 Count the Colors 线段树
    poj 2528 Mayor's posters 离散化 线段树
    hdu 1599 find the mincost route floyd求最小环
    POJ 2686 Traveling by Stagecoach 状压DP
    POJ 1990 MooFest 树状数组
    POJ 2955 Brackets 区间DP
    lightoj 1422 Halloween Costumes 区间DP
    模板 有源汇上下界最小流 loj117
    模板 有源汇上下界最大流 loj116
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819398.html
Copyright © 2011-2022 走看看