洛谷p2018 树DP

首先,本做法和其他的O(N^2logn)做法是完全不一样的,那种做法比较好理解,我说一下我的做法,感jio是O(Nlogn),不太会算
首先思考如何获得每一点要如何知道由当前点出发的答案是什么?对于绝大部分点,都有父亲节点和儿子节点,那么对于每一个点都是这么抉择走的。

这里先不看now点,看节点1,如果第一个人从节点1开始要怎么走呢?就是花4次时间, 分别走4跳荧光笔的路径对吧,然后就没节点1什么事情了,接下来的路要靠其他点他们自己去走。
我们假设f[i]为从i点开始往下走完所有节点的最短时间
g[i]为从i点开始往上走完所有节点的最短时间
那么对于上图而言,要获得节点1的答案就需要获取f数组和g数组。

现在我们考虑如何得出这两个东西。
先思考f[i]怎么获得,f[i]是往下走的所有节点的最短时间,那么对于每一个i节点,我们可以对他所有的儿子f[z]去进行排序!!然后从f[z]比较大的这个儿子节点先走,在走其他的,从大到小走,为什么要这么走呢?这是用了个贪心的做法!!!从一个点要走的步数多,那么你先走他就可以比较快的完成嘛!,所以排个序后贪心的拿就好了,第一个拿的就加0,第二个拿的就加1(因为要多花额外的时间才能走这条路线是不是),。。。。。。最后取一个max,一个点的完成的时间取决于最慢的那个时间。
f[i] = max(f[son] + v)

下一步是考虑g[i],思考一下g[i]怎么来的还是这个图

现在我们看now点,要算g[now]就是要算now点往上要多久才能覆盖全部,所以类比f数组的排序操作,我们需要对
1 - 2,1 - 3, 1 - 4这些边进行排序,然后计算附加权值取max,这些都和f一样,只是不同的是1-now这条边的必定要先走,因为一个点只有一个父亲嘛,你往上走就只会走这条路,然后走到now点的父亲节点就出现了分支,你就需要对这些分支去进行进行排序操作取max就可以得到g啦。
在这个图里面,就是比较g[2], f[3],f[4]的大小,这些你看是不是我们在之前都算出来了呢。

最后答案什么算?很遗憾我们不能简单粗暴的用这个

value[i] = max(max(f[i], g[i]), min(f[i], g[i]) + 1);
		ans = min(ans, value[i]);

这样就是对于先走下面全部走完在走上面或者先走上面在走下面,这样子会wa一个点(就是他数据水才AC这么多),你可能是先在下面走一两条边, 在走上面,在继续走下面, 所以还需要对于该点连着的所有边进行一次排序操作就可以了。
对于上面那个图,要算节点1,就是对g[2],f[3],f[now],f[4]去排序算权值就好了。
这样总来说是就是跑两遍dfs就可以出答案拉。感觉就是nlogn,如果不是请教我算~最后代码方面不开O2是跑了48ms,开了o2是跑了33ms

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<time.h>
#include<string>
#include<stack>
#include<cmath>
#include<map>
#include<set>
#define ll long long
//#define double long double
using namespace std;
#define PI 3.1415926535898 
#define eqs 1e-6
const long long max_ = 5000 + 7;
const int mod = 1e9 + 7;
const int inf = 1e9;
const long long INF = 1e18;
int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
int min(int a, int b) {
	return a < b ? a : b;
}
int max(int a, int b) {
	return a > b ? a : b;
}
bool cmp(int a, int b) {
	return a > b;
}
int temp[max_], tn = 0, n, f[max_], g[max_], father[max_], value[max_];
vector<int> xian[max_];
void dfs(int now, int fa) {
	father[now] = fa;
	//算i往下需要多少才可以传递完所有人
	for (int i = 0; i < xian[now].size(); i++) {
		int to = xian[now][i];
		if (to == fa)continue;
		dfs(to, now);
	}
	tn = 0;
	for (int i = 0; i < xian[now].size(); i++) {
		int to = xian[now][i];
		if (to == fa)continue;
		temp[++tn] = f[to];
	}
	sort(temp + 1, temp + 1 + tn, cmp);
	int v = 0;
	for (int i = 1, j = 1; i <= tn; j++, i++) {
		v = max(v, temp[i] + j);
	}
	f[now] = v;
}
void dfs2(int now, int fa) {
	tn = 0;
	//首先花费1的价值到达父亲节点,然后判断从父亲节点往哪里走
	if (father[fa])
		temp[++tn] = g[fa];
	for (int i = 0; i < xian[fa].size(); i++) {
		int to = xian[fa][i];
		if (to == father[fa] || to == now)continue;
		temp[++tn] = f[to] + 1;
	}
	sort(temp + 1, temp + 1 + tn, cmp);
	int v = 0;
	for (int i = 1, j = 0; i <= tn; j++, i++) {
		v = max(v, temp[i] + j);
	}
	g[now] = v; if (now != 1)g[now]++;
	for (int i = 0; i < xian[now].size(); i++) {
		int to = xian[now][i];
		if (to == fa)continue;
		dfs2(to, now);
	}
}
int main() {
	n = read();
	for (int i = 2; i <= n; i++) {
		int a = read();
		xian[a].push_back(i);
		xian[i].push_back(a);
	}
	dfs(1, 0);
	dfs2(1, 0);
	int ans = f[1];
	value[1] = f[1];
	for (int i = 2; i <= n; i++) {
	/* value[i] = max(max(f[i], g[i]), min(f[i], g[i]) + 1); ans = min(ans, value[i]);*/
		tn = 0;
		temp[++tn] = g[i] - 1;
		for (int j = 0; j < xian[i].size(); j++) {
			int to = xian[i][j];
			if (to == father[i])continue;
			temp[++tn] = f[to];
		}
		sort(temp + 1, temp + 1 + tn, cmp);
		int v = 0;
		for (int x = 1, j = 1; x <= tn; j++, x++) {
			v = max(v, temp[x] + j);
		}
		value[i] = v;
		ans = min(ans, value[i]);
	}
	cout << ans + 1 << endl;
	for (int i = 1; i <= n; i++) {
		if (value[i] == ans) {
			cout << i << " ";
		}
	}
	
	return 0;
}
全部评论

相关推荐

努力学习的小绵羊:我反倒觉得这种挺好的,给不到我想要的就别浪费大家时间了
点赞 评论 收藏
分享
在评审的大师兄很完美:像这种一般就是部门不匹配 转移至其他部门然后挂掉 我就是这样被挂了
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务