您的位置:首页 > 其它

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

2015-11-14 20:34 295 查看
题目链接: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-2。ri >= 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))的话,需要遍历选取次数、i和j。这个复杂度应该是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\n", 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: