洛谷 P2018 消息传递 题解

题面

总体来说是一道从下往上的DP+贪心;

设f[i]表示将消息传给i,i的子树全部接收到所能消耗的最小时间;

那么对于i的所有亲儿子节点j,我们会贪心地先给f[j]大的人传递,然后次大.....

可以证明,这样的答案一定是最优的;

然后f[i]=max(f[i],f[j]+cnt);

总的时间复杂度是O(n^2logn),可过;

但是还可以进一步优化(窝太懒了所以没写)

换根法,可以一遍dfs(nlogn)就求出所有的答案;

#include <bits/stdc++.h>
#define inc(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;
int head[2010],cnt;
class littlestar{
	public:
	int to,nxt;
	void add(int u,int v){
		to=v; nxt=head[u];
		head[u]=cnt;
	}
}star[4010];
int n;
int f[2010];
class node{
	public:
	int value,pos;
};
vector<node> vec[2010];
bool cmp(node x,node y)
{
	return x.value>y.value;
}
void dfs(int u,int fa)
{
	for(int i=head[u];i;i=star[i].nxt){
		int v=star[i].to;
		if(v==fa) continue;
		dfs(v,u);
		vec[u].push_back((node){f[v],v});	
	}
	sort(vec[u].begin(),vec[u].end(),cmp);
	inc(j,0,(int)vec[u].size()-1){
		int v=vec[u][j].pos;
		f[u]=max(f[u],f[v]+j+1);
	}
}
int tmp[2010];
int main()
{
	scanf("%d",&n);
	inc(i,2,n){
		int tmp; scanf("%d",&tmp);
		star[++cnt].add(tmp,i);
		star[++cnt].add(i,tmp);
	}
	int minn=INT_MAX;
	inc(i,1,n){
		memset(f,0,sizeof(f));
		inc(j,1,n) vec[j].clear();
		dfs(i,0);
		if(f[i]<minn){
			tmp[0]=0;
			tmp[++tmp[0]]=i;
			minn=f[i];
		}
		else if(f[i]==minn){
			tmp[++tmp[0]]=i;
		}
	}
	printf("%d\n",minn+1);
	inc(i,1,tmp[0]){
		printf("%d ",tmp[i]);
	}
}
/*
8
1
1
3
4
4
4
3
*/

猜你喜欢

转载自www.cnblogs.com/kamimxr/p/11808330.html