HDU

来源:互联网 发布:木工简易算法在线阅读 编辑:程序博客网 时间:2024/06/05 20:02

题目描述:

点击打开链接

要你构造一棵结点数为n的树,给了结点度数为i是可以得到的分数vi,现在要求你构造的这棵树能够得到的分数最大。这个问题看起来有点复杂,我们可以把这个问题转换一下,首先如果要构造一棵树的话,那么所有结点的总度数一定为2*n-2,我们可以把不同度数不同分数的结点看作n-1种不同的结点,那么实际上我们要做的工作就是把这2*n-2度分配给这n-1种不同的结点从而得到最大的分数,这个问题可以进一步转化成,我们需要从n-1种不同的结点中选择若干个使其度数和为2*n-2,如果把每种结点的度数看作花费,分数看作价值,2*n-2看作背包的最大容量的话,这其实就是非常典型的完全背包问题!但是这个背包问题是有限制条件的,1.你选择物品的数目必须为n个,2.背包的容量2*n-2你必须用完,3.你不能全部选择花费为1的物品,因为实际上一棵树不可能所有结点的度数都为1。那么我们所需要解决的就是这三个问题。如果暴力去解决的话,很明显复杂度是n^3,绝对会T,那么就想优化。首先设计状态dp[i]表示花费为i时的最大价值。如果必须选择n个物品,我们可以采取预处理,我们先选择n个花费为1的物品,那么把剩下物品的花费都-1,价值都减去a[1],把dp[0]初始化为n*a[1],为什么这样做呢,dp[0]表示花费为0时的最大价值,初始化为a[1]*n的话就表示我们是在已经选了n个物品1的情况下在去选择后面的物品,这时我们已经保证了选择了n个物品,那么我们把剩下的物品的花费和价值都减去对应的值,那么我们在进行DP时实际进行的操作是我们要选择多少物品去替代已经选择的物品1这样不管怎么选我们都保证了物品一定是n个。然后我们还剩下n-2的容量,我们一定要至少选择一个物品并且保证这n-2的容量用完,这里就要用到一个小技巧,我们只需要把dp数组的数初始化为-inf就可以保证了。其中的原理也很明显,我们在正常写n^3DP时保证容量一定用完的处理方式就是用一个量来记录当前用了多少容量,并且状态转移保证从已有的物品组合方式转移得到新状态,那么这里我们把所有数初始化为-INF的话,在进行完全背包时,如果dp[j-i]=-INF,也就是说这种即使加上一个数也依然是一个很大的负数,那么其实这种不严格的转移方式在最后筛答案的时候就自然会被淘汰掉,具体的需要自己想一下就好了。

AC代码:

#include<iostream>#include<sstream>#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<queue>#include<algorithm>using namespace std;const int INF=0x3f3f3f3f;const int MAXM=10010;int n;int a[MAXM];int dp[MAXM];int main(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        for (int i=1;i<=n-1;i++)            scanf("%d",&a[i]);        for (int i=0;i<MAXM;i++)            dp[i]=-INF;        dp[0]=a[1]*n;        for (int i=2;i<=n-1;i++)            a[i]=a[i]-a[1];        for (int i=1;i<=n-2;i++) {            for (int j=i;j<=n-2;j++) {                dp[j]=max(dp[j],dp[j-i]+a[i+1]);            }        }        printf("%d\n",dp[n-2]);    }    return 0;}



原创粉丝点击