kingdom------(dp)

kingdom

https://ac.nowcoder.com/acm/problem/19810

我们先根据题意一步一步分析,由于只是给出树的节点个数,树可以由我们任意构造,我们假设n个节点取得答案最大的时候树长这样
图片说明
根节点root,连接着k个子树,k个子树大小分别为sum1,sum2.....sumk,有
图片说明
我们令ans[n]为n个节点的树所得的最优解
root的“心腹”其实就是root的重儿子,我们root的重儿子是所在子树是X,重儿子中全部节点到root的费用,是不是就是ans[sumX],因为经过重儿子的边到父节点不会有任何影响。如果不是重儿子的所在子树Y(Y不等于X),这个子树的全部节点到root的贡献,是否都需要+1(题意转换,不理解的话仔细读题),因此这个子树的全部点到root的费用,就是ans[sumY]+sumY,sumY个节点,每个都经过边一次。因此,我们可以得出一个结论,
假设重儿子所在子树为X,有
图片说明
这里的减1是减去root这个根节点。
这里我们设一个二维数组,dp[i][j]表示 i个节点组成的森林(多个树),且节点最多的那个树节点数不超过j,注意!!!注意!!!是森林,森林,森林,重要的事情要说三遍!!!!可能不止一棵树,上图
图片说明
那么现在问题是如何维护ans[]和dp[][]这两个数组?
由上面那幅图我们可以清晰得出,只要枚举n个节点的重儿子所在子树的节点树sumX,
然后可以得到一个可能结果图片说明
因此,对于ans[n],只需要枚举sumX不断更新,取最大值,得出公式
图片说明
现在只剩下dp[][]数组了,我们来看,dp[i][j],节点总数为i,最大树节点数小于等于j的森林,有以下两种可能:
图片说明 ,小于等于j-1必定小于等于j,可以直接更新
图片说明x<=j, 由一棵节点数为x的树一个节点总数为i-x的森林,且这个森林的节点最多的那棵树不超过j 组成 节点总和为i,节点最多的树不超过j的森林
取上面两种情况的最小值即可,由于数据是8000,复杂度不能超过n^2,第一重for循环肯定是枚举节点总数i = 1~n,然后枚举sumX,求得ans[i],然后更新dp[][],由于dp和ans[x]这一变量有关,类似于完全背包,在枚举的时候会得到一个新的物品ans[i],枚举放入这个背包,背包容量是n,然后更新即可,完结,撒花★,°:.☆( ̄▽ ̄)/$:.°★

#include <cctype>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <list>
#include <map>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#define ll long long
#define pll pair<long long, long long>
#define P pair<int, int>
#define PP pair<P, P>
#define eps 1e-10
#define PI 3.1415926535898
#define It set<node>::iterator
using namespace std;
const int maxn=8e3+10;
int dp[maxn][maxn],ans[maxn];
int main() {
    int n;
    scanf("%d",&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);
        }
        for (int j=1; j<=n; j++) {
            dp[j][i]=dp[j][i-1];
            if (j>=i) dp[j][i]=max(dp[j][i],dp[j-i][i]+ans[i]);
        }
    }
    printf("%d\n",ans[n]);
    return 0;
}
全部评论
大佬,请问一个问题。在优化dp[i][j]=dp[i−x][j]+ans[x]这部分中,dp[i][j] = max(dp[i-1][j]+ans[1], dp[i-2][j]+ans[2],...,dp[i-(j-1)][j]+ans[j-1],dp[i-j][j]+ans[j]) 真的就等价于dp[i][j]=max(dp[i][j-1], dp[i-j][j]+ans[j]);吗?感觉好抽象,不理解,大佬可以解释一下吗?(因为这个潜台词就是ax(dp[i-1][j]+ans[1], dp[i-2][j]+ans[2],...,dp[i-(j-1)][j]+ans[j-1])=dp[i][j-1])
点赞 回复 分享
发布于 2020-08-17 21:09
上面可能说得太啰嗦,即怎么证明max(dp[i-1][j]+ans[1], dp[i-2][j]+ans[2],...,dp[i-(j-1)][j]+ans[j-1])=dp[i][j-1]?
点赞 回复 分享
发布于 2020-08-17 21:21
您可以试着看作一个背包问题,背包处理时,一维的01背包,有for (int i=n; i>=v; i--) dp[i]=max(dp[i],dp[i-v]+ans[v]); 这个也类似这样的方法dp【i】【j】 体积总和为 i,最大物品体积是 j 的最大价值,for(int i=n; i>=x; i--) dp[i][j]=max(dp[i][j],dp[i-x][j]+ans[x]),当然还需要保证x<=j, 那种很正式的证明我也不太会😟😟😟
点赞 回复 分享
发布于 2020-08-17 21:56

相关推荐

微风不断:兄弟,你把四旋翼都做出来了那个挺难的吧
点赞 评论 收藏
分享
头像
11-09 17:30
门头沟学院 Java
TYUT太摆金星:我也是,好几个华为的社招找我了
点赞 评论 收藏
分享
10 收藏 评论
分享
牛客网
牛客企业服务