【每日一题】 kingdom

kingdom

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

动态规划。

根据题意直接构造出树比较难,所以采用动态规划的方法。

记ans[i]表示当树有i个结点时所求花费的最大值。
假设在考虑有i个结点时,我们已经知道了结点数小于i时的ans,则可以通过ans[i]=max(ans[i],ans[j]+???)得到答案,其中j为结点数为i时所有可能的重儿子,也就是题目中所说的“官员的心腹”。现在只需将“???”求出来。

理解“???”是什么意思,不妨先看看转移方程中缺什么。在选定了重儿子的同时其贡献也求了出来,即ans[j],现在需要安排轻儿子:求出轻儿子的贡献同时确保其小于重儿子。这就要求将除重儿子以外的结点分成若干组,每组结点数不能大于j。因此,我们再定义:f[i][j]表示将i个结点分成若干组(每组各有一个根节点)且每组不大于j个时,得到的最大花费。

由此第一个转移方程便求出来了:ans[i]=max(ans[i],ans[j]+f[i-j-1][j]+i-j-1)。
为什么要加上后面“i-j+1”这一串?因为f[i][j]分组后每组实际上是一棵子树,现在需要将子树连接到根节点上,由于其为轻儿子(也就是非心腹大臣),自然要在子树的根节点上加一,而后所有子树便都要加一,其大小正好为“i-j+1”。

下面考虑第二个转移方程,最直接的想法是:f[i][j]=f[i-j][j]+ans[j]。显然这没有考虑到所有的情况。观察第一个转移方程,f[i-j-1][j]中,f[][]两个方括号里的大小关系是不缺定的,而在第二个转移方程中的“i-j”就直接说明了i>=j,得到的f[i][j]范围肯定是小了。

所以,现在要考虑当i<j时的情况。咋考虑?当然要从f的定义入手。
f[i][j]是要将i个分组,每组不超过j个。那么现在假设i已经小于j了,将i个分组,每组不超过j个,j+1个,j+100个,都是一样的,一组最多只有i个。
所以第二个转移方程如下:
f[i][j]=f[i-j][j]+ans[j]; //(i>=j)
f[i][j]=f[i][j-1]; //(i<j)

最后通过两个转移方程即可逐步求出ans[n]。

代码:

#include <iostream>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <functional>
#include <string>
#include <cstring>
#include <sstream>
#include <deque>
#define fir first
#define sec second
using namespace std;

typedef long long ll;
typedef pair<int,int> P;
typedef pair<P,int> Q;

const int inf1=2e9+9;
const ll inf2=8e18+9;
const ll mol=1e9+7;
const int maxn=8e3+9;
const ll maxx=1e12+9;

int n;
int ans[maxn],f[maxn][maxn];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
            ans[i]=max(ans[i],ans[j]+f[i-j-1][j]+i-j-1);
        for(int j=1;j<=n;j++)
        {
            if(i>=j) f[i][j]=f[i-j][j]+ans[j];
            else f[i][j]=f[i][j-1];
        }
    }
    printf("%d\n",ans[n]);
}
全部评论

相关推荐

11-04 10:30
已编辑
门头沟学院 研发工程师
开心小狗🐶:“直接说答案”
点赞 评论 收藏
分享
10-19 10:28
已编辑
成都理工大学 后端工程师
团孝子已上线feeling:面了很多家公司,能感受到目前只有小公司+外包喜欢问八股。大厂虽然也问八股,但是是从实习、项目中进行提问,并且大厂会问很深,面试官也会对你的回答进行思考➕追问,所以准备大厂面试前一定要备好相关资料。对于算法,我做的是codetop前100+力扣hot100+力扣高频150,面试中实感hot100就足够,基本上只要是hot100就秒答。对于项目和八股,我做的也是烂大街的星球项目,八股则是看小林和问ai,自己也写了很多技术博客和画了很多思维导图,并且自己也尝试用嘴巴说出来,不只停留于纸面。运气也很重要,必须要让面试官/HR看到简历才行,所以建议投递时间是下午两点。tl:第一岗位9.9&nbsp;投递9.10&nbsp;一面(一面评价:最近见过最强的大三,结束五分钟后约二面,都晚上九点了不下班吗)9.11&nbsp;二面(三道算法a出两道,反问评价:经验不够等横向,我实习生要啥经验)9.21挂(实习时间过短+其他原因,想要一年实习的,为什么不招个正职)第二岗位10.10投递10.11约面(主管打电话,说看到我之前投递记录了想要我挂qa职进去干后端,同意)10.14&nbsp;一面(无八股,主动说确实很强,意愿很强)10.16&nbsp;oc其余,友邦,东软,东华,惠择,用友oc已拒京东测开一面挂(投后端被测开捞)腾讯测试已拒(投后端被测开捞)ps:表扬惠择的主管面,没怎么问技术(可能是一面面试官沟通过了),全程一起讲大道理,解答了心中很多疑惑,也告诉我以面试官角度来看怎么选候选人,如果可以下次一定选惠择
HeaoDng:美团好像可以触发一面通
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务