【每日一题】7月13日题目精讲—Kingdom

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 1048576K,其他语言2097152K
64bit IO Format: %lld

题目描述

X王国有n位官员,编号从1到n。国王是1号官员。除了国王以外,每个官员都有一个上司。我们称这个官员是这个上司的下属。上司的编号总比下属小。
我们定义一个官员的影响力为他所有下属的影响力之和再加1。例如,一个没有下属的官员的影响力是1。国王的影响力总是n。
任何一位有下属的官员总是选择他的下属中影响力最高的作为他的心腹(有若干下属影响力相同的话则会选择编号最小的)。
一位官员得到一条消息后,他就要把消息传达给国王。我们定义一位官员的花费为他将消息传达给国王的花费。国王自己的花费为0。如果一位官员是他上司的心腹,则他的花费等于他上司的花费,否则他的花费为他上司的花费加1。
由于时代和平,消息并不需要传递的太快。我们希望你决定每位官员(除了国王)的上司,使得所有官员的花费之和尽量大。

输入描述:

一个整数n(1≤ n≤ 8000)表示包括国王在内的官员的总数。

输出描述:

一个整数表示最大的花费之和。

示例1
输入
复制

4

输出
复制

2

题解:

借鉴自题解
重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点
假设n个节点的排列情况如图所示

树根是root,下面是k个子树
ans[n]为n个节点的数所得的最优解
根据题意我们知道点x的心腹就是点x的重儿子
如果重儿子所在子树A,我们将重儿子的子树a与x点分离,看做独立部分,那么子树a的最优解就是ans[sumA]
我们再把子树a连接到x点,因为子树a是重儿子,重儿子的边到父节点木有影响,所以最优解还是ans[sumA]
如果子树A不是重儿子,我们也是先分离看,最优解是ans[sumA],然后连接x点,因为不是重儿子(即心腹),根据题意花费要加一,相当于子树A中的节点都要加一,加了sumA个1,最优解就是ans[sumA]+sumA
可以结合下图理解理解
如果重儿子所在子树是A,其他点就不是重儿子,所得结论:
ans[n]=(ans[sum1]+ans[sum2]+…ans[sumA]…+ans[sumk])+(n−sumA−1)

然后我们用dp[i][j]表示i个节点组成的多个树,且最多节点的那棵树节点数不超过j
(该图选自参考题解)

我们现在要用到ans和dp两个数组
根据图片可得:dp[n−sumX−1][sumX]+(n−sumX−1)+ans[sumX]
这样ans[n]取最大值即可
ans[n]=dp[n−1−sumX][sumX]+(n−1−sumX)+ans[sumX],且1<=sumX<n
dp[i][j]表示i个节点组成的多个树,且最多节点的那棵树节点数不超过j,分为两种情况:
dp[i][j]=dp[i][j-1]//节点数是i,最大树节点不超过j-1也肯定不超过j
dp[i][j]=dp[i-x][j]+ans[x]//一颗节点数为x的树和一个节点总数为i-x的森林,合成一个节点数为i的森林,最大节点依旧小于j
两种情况取最小值

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=8005;
int dp[maxn][maxn];
int ans[maxn];
int main()
{
   
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
   
		for(int j=1;j<i;j++)
		{
   
			ans[i]=max(ans[i],ans[j]+dp[i-j-1][j]+(i-j-1));//求出i个节点的树的最优解 
			//重儿子所在子树节点数为j
			//非重儿子所在子树节点个数总和为i-j-1 
		}
		for(int j=1;j<=n;j++)//有点像完全背包 
		{
   
			if(j>=i)
		 		dp[j][i]=max(dp[j][i-1],dp[j-i][i]+ans[i]);
			else 
				dp[j][i]=dp[j][i-1];
		}
	}
	cout<<ans[n];
	return 0;
}