ACM学习历程—HDU 5534 Partial Tree(动态规划)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5534

题目大意是给了n个结点,让后让构成一个树,假设每个节点的度为r1, r2, ...rn,求f(x1)+f(x2)+...+f(xn)的最大值。

首先由于是树,所以有n-1条边,然后每条边连接两个节点,所以总的度数应该为2(n-1)

此外每个结点至少应该有一个度。

所以r1+r2+...rn = 2n-2ri >= 1;

首先想到让ri >= 1这个条件消失:

xi = ri,则x1+x2+...xn = n-2

然后把所有f的脚标减一。即新f(i) = f(i+1)

这样就相当于总和一定,求新f的和的最大值。而且与x的大小顺序无关。

但是到这里利用p(i) = max(p(i), p(i-j)+f(j))的话,需要遍历选取次数、ij。这个复杂度应该是n^2(n-1)/2,是n^3级别的复杂度。

考虑到0取和不取,虽然不影响i的大小,但是会影响p(i)的大小,而且一个数取了一个0之后,就会少一个数。

于是又有一个消除0的条件:

f(i) = f(i)-f(0),这样取0的贡献就是0,但是其他值的贡献是与f(0)的差值。

那么最后答案加上原f(0)*n即可。

然后就发现没了0的贡献后,其他值都随便取,而且不会超过n个数。

然后就类似于完全背包一样,利用p(i) = max(p(i), p(i-j)+f(j))即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <string>
#define LL long long

using namespace std;

const int maxN = 2050;
int n, f[maxN], p[maxN], ans;

void input()
{
    scanf("%d", &n);
    scanf("%d", &f[0]);
    for (int i = 1; i < n-1; ++i)
    {
        scanf("%d", &f[i]);
        f[i] -= f[0];
    }
    ans = f[0]*n;
    f[0] = 0;
    memset(p, -1, sizeof(p));
    p[0] = 0;
}

int myMax(int x, int y)
{
    if (x == -1) return y;
    else return max(x, y);
}

void work()
{
    for (int i = 0; i <= n-2; ++i)
        for (int j = i; j <= n-2; ++j)
            p[j] = myMax(p[j], p[j-i]+f[i]);
    ans += p[n-2];
    printf("%d
", ans);
}

int main()
{
    //freopen("test.in", "r", stdin);
    int T;
    scanf("%d", &T);
    for (int times = 0; times < T; ++times)
    {
        input();
        work();
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/andyqsmart/p/4965009.html