链接
题意
给出一个数n,和一组关于度(degree)的函数f(d),若某点的度为d,则提供f(d)的贡献,问n个点构成的树中最大贡献值。
题解
2015年长春赛区的1006,很不错的题目。
首先要知道一颗含有n个节点的树结构,其所有节点的度之和为2*(n-1),因为只有n-1条边;并且只要保证每个节点的度都>0,总度为2*(n-1),则一定有一个树结构对应。
由于每个点需要至少度为1,则成了,将n-2个度分给n个点,点的贡献和sum(f(di))最大为多少。
这可以转化成一个背包问题d[i][j],代表前i个节点分配j个度所获得的最大贡献值。但是这么做是超时的。
可以以度为转移的对象,容量变为n-2,度一共有n-2种,消耗分别为[1, n-2],价值为[f(2), f(n-1)],相同种类的度可以取无限种,容量不超过n-2即可(当然取的次数也不会超过n-2),变成了一个完全背包问题。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long lint;
const lint oo = 1e16;
int f[2020];
lint dp[2020];
int main()
{
int T;
cin >> T;
while(T--)
{
int n;
cin >> n;
for(int i = 1; i <= n-1; i++)
scanf("%d", &f[i]);
for(int i = n-2; i >= 0; i--)
dp[i] = -oo;
dp[0] = n * f[1];
for(int i = 1; i <= n-2; i++) /// Cost-type
{
for(int j = 0; j <= n-2; j++) /// V
{
if(j >= i) dp[j] = max(dp[j], dp[j - i] + f[i + 1] - f[1]);
}
}
cout << dp[n-2] << endl;
}
return 0;
}