zoukankan      html  css  js  c++  java
  • HDU 5534 Partial Tree 完全背包

    题意:

    让你构造一个有(n(2 leq n leq 2015))个节点的树。
    然后定义这棵树的(coolness)(sum{f(d)}),其中(d)是每个节点的度数,函数(f)在输入中给出。

    分析:

    一颗含有(n)个节点的树,有(n-1)条边,度数之和为(2n-2)
    所以我们可以转化成一个完全背包问题:

    背包的容量为(2n-2),我们要恰好选(n)个物品而且要恰好装满背包。体积为(i)的物品的价值为(f(i)),而且每种物品有无穷多个。
    所以可以设计出类似的状态:(d(i, j))表示选了(i)件物品,体积为(j)时所能得到的最大价值。
    最后发现总的时间复杂度是(O(n^3))的。

    分析原因:问题就在于转移的过程会有很多相同的状态。比如背包里的物品为((1, 2, 3)),这个状态可以由((1, 2))后面加个(3)得到,也可以由((1, 3))后面加个(2)得到。事实上具体是由什么顺序转移过来我们是不关心的,我们只关系每种物品选了多少件。

    参考叉姐的思路
    一开始假设大家的度数都为(1),然后慢慢地增大,让大家的度数和增大到(2n-2)
    设计新的状态(d(i, j))表示已经考虑过增大到(leq i)的度数,总度数和为(j),能得到的最大价值。
    那么有状态转移方程:$d(i,j)=max { (d(i-1,j),d(i,j-(i-1))+f(i)-f(1)) } ( 最终所求的答案就是)d(n-1,n-2)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 2050;
    
    int n;
    int f[maxn];
    //滚动数组优化一下空间
    int d[2][maxn];
    
    int main()
    {
    	int T; scanf("%d", &T);
    	while(T--) {
    		scanf("%d", &n);
    		for(int i = 1; i < n; i++) scanf("%d", f + i);
    		memset(d, 0, sizeof(d));
    		int cur = 1;
    		d[1][0] = n * f[1];
    		for(int i = 2; i <= n - 1; i++) {
    			cur ^= 1;
    			memset(d[cur], 0, sizeof(d[cur]));
    			for(int j = 0; j < i - 1; j++) d[cur][j] = d[cur^1][j];
    			for(int j = i - 1; j <= n - 2; j++)
    				d[cur][j] = max(d[cur^1][j], d[cur][j-i+1] + f[i] - f[1]);
    		}
    
    		printf("%d
    ", d[cur][n - 2]);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    剑指Offer——斐波那契数列
    剑指Offer——旋转数组的最小数字
    剑指Offer——用两个栈实现队列
    剑指Offer——重建二叉树
    剑指Offer——从尾到头打印链表
    剑指Offer——替换空格
    Leetcode 153. Find Minimum in Rotated Sorted Array -- 二分查找的变种
    头条笔试后端开发
    渣浪电话面
    Leetcode 中Linked List Cycle 一类问题
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4929102.html
Copyright © 2011-2022 走看看