[gmoj 3501] 【NOIP2013模拟联考15】消息传递

在这里插入图片描述
思路:
首先看到题目不难想到直接枚举每一个点作为根进行DP,方程如下
在这里插入图片描述
其中 order 表示选择的顺序为 1,2,3,4…。
在此直接贪心让最大的 f j f_j fj 匹配最小的 o r d e r j order_j orderj即可。

这样直接做树形DP时间复杂度是 O ( n 2 log ⁡   n ) O(n^2 \log \ n ) O(n2log n)的,
但并不是本题的最优解。
(当然应为本题的 n ≤ 1000 n \leq 1000 n1000,这样的算法可以通过)。

考虑如何优化:

我们若仔细思考上面的DP过程不难发现:
若对于 a ≠ b a \not= b a=b 有 a 做根和 b 做根时的 s o n x son_x sonx相同 ,
那么我们就可以直接拿第一次计算出来的 f x f_x fx直接作为返回值,
这样就可以减少许多运算量。
具体化上面的过程:对于每个相同的 s o n x son_x sonx
他们都会对应相同的 f a t h e r x father_x fatherx ,可以用 d p [ x ] [ f a t h e r x ] dp[x][father_x] dp[x][fatherx]记录答案,
这样记忆化搜索就完成了。
但是空间复杂度不够优,我们把 d p [ x ] [ f a t h e r x ] dp[x][father_x] dp[x][fatherx] f a t h e r x father_x fatherx
连向 x 的单向边代替,这样空间只要开 O ( n ) O(n) O(n) 即可。

来大致算一下这样的时间复杂度:
第一次DP时可以把 n-1条边的DP值纪录,显然一次DP的复杂度是 O ( n log ⁡ n ) O(n \log n) O(nlogn) ,然而我们只有 2n-2 条单向变,所以总时间大概相当于两次的第一次DP,复杂度仍然是 O ( n log ⁡ n ) O(n \log n) O(nlogn)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
const int N = 2e6 + 10;
struct edge {int next, to;} e[N * 2];
int n, head[N], tot, dp[N], ins[N], minn = 2147483647,  a;
void add(int x, int y) {e[++tot] = (edge){head[x], y}; head[x] = tot;}
int dfs(int x, int f, int fr)
{
	if(fr && dp[fr]) return dp[fr]; 
	int res = 0;
	priority_queue <int> q;
    for(int i = head[x]; i; i = e[i].next)
	{
        int v = e[i].to;
        if(v == f) continue;
        q.push(dfs(v, x, i));
    }
    for(int i = 1; !q.empty(); i++, q.pop())
        res = max(res, q.top() + i);
    return dp[fr] = res;
}
int main()
{
	freopen("news.in", "r", stdin);
	freopen("news.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 2; i <= n; i++)
        scanf("%d", &a), add(a,i), add(i,a);
    for(int i = 1; i <= n; i++)
	{
        ins[i] = dfs(i, 0, 0);
        minn = min(minn, ins[i]);
    }
    printf("%d\n", minn + 1);
    for(int i = 1; i <= n; i++) if(minn == ins[i]) printf("%d ",i);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值