【消息传递】解题报告

3.消息传递

【问题描述】

巴蜀国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果A是B的上级,B是C的上级,那么A就是C的上级。绝对不会出现这样的关系:A是B的上级,B也是A的上级。

  最开始的时刻是0,你要做的就是用1单位的时间把一个消息告诉某一个人,让他们自行散布消息。在任意一个时间单位中,任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属。

现在,你想知道:

1.到底需要多长时间,消息才能传遍整个巴蜀国的所有人?

2.要使消息在传递过程中消耗的时间最短,可供选择的人有那些?

【文件输入】

输入文件的第一行为一个整数N(N≤1000),表示巴蜀国人的总数,假如人按照1到n编上了号码,国王的编号是1。第2行到第N行(共N-1行),每一行一个整数,第i行的整数表示编号为i的人直接上级的编号。

【文件输出】

文件输出共计两行:

第一行为一个整数,表示最后一个人接到消息的最早时间。

第二行有若干个数,表示可供选择人的编号,按照编号从小到大的顺序输出,中间用空格分开。

【样例输入】

8

1

1

3

4

4

4

3

【样例输出】

5

3 4 5 6 7



感觉很基础的一道题,但是没敢下手,就是卡在了一个点上,父亲传递给儿子的时候(枚举每一个点为根节点),每一个的时间是不同的,当时卡在了这一点上,没有想出解决方案。实际上只需要一个贪心就够了,也就是尽早给时间比较久的传递,因此(覃禹舜的选排)按降序排序就好了,f[i] = max{f[j]+k}j是i的儿子,k为排序的序号。


一开始的时候考虑的是,求一条直径,在直径上找点,但是仔细看题,给每个儿子传递的时间是不同的,因此用前序的搜索是无法解决的,因为传递给儿子的顺序是要由儿子的时间来决定的,因此这个策略是错误的。从这里启发写树形DP。


这道题再次使用到欧教讲过多次的技巧。一棵形状不确定的树,不用建出来,只需要在搜索的时候用标记变量记录走过的点,即能实现动态地建一棵树。这道题同样,因为从一个点出发,必须走遍其他所有的点(这一点不同于前两天那一道treedp,因为那一道题可以选择不走某一棵子树,因此一次dp就行了,在路上维护ans值),因此必须进行n次dp,每次以i为根,解为f[i]。


讨论了这些这些之后,就没有多少技巧性的东西,就是一个简单的treedp。

就是卡在了

qsort(s[l]+1,s[l][0],sizeof(s[1][0]),&bigger);
for (long i=1;i<s[l][0]+1;i++)
{
f[l] >?= s[l][i]+i;
}

这部分上。


有什么启示呢?有什么启示呢?我遇到瓶颈了。。。感觉这种时候反而更需要打开思维才行啊,局限了。而且有时候就只是一个很小的跳跃而已。


/AC

#include <cstdio>
#include <cstdlib>
struct node
{
	long index;
	node* next;
};


node* peo[1002];
long n;
const long oo = 0x7fff0000;
long s[1002][1002];
long f[1002];
bool used[1002];


int bigger(const void* a,const void* b)
{
	long aa = *(long*)a;
	long bb = *(long*)b;
	
	if (aa>bb) return -1;
	if (aa<bb) return 1;
	return 0;
}


void dfs(long l)
{
	if (f[l]>-oo) return;
	node* ths = peo[l];
	while (ths)
	{
		if (!used[ths->index])
		{
			used[ths->index] = true;
			dfs(ths->index);
			s[l][++s[l][0]] = f[ths->index];
			used[ths->index] = false;
		}
		ths = ths->next;
	}
	if (s[l][0]==0)
	{
		f[l] = 0;
		return;
	}
	qsort(s[l]+1,s[l][0],sizeof(s[1][0]),&bigger);
	for (long i=1;i<s[l][0]+1;i++)
	{
		f[l] >?= s[l][i]+i;
	}
}


void insert(long a,long b)
{
	node* tmp = new node;
	tmp->index = b;
	tmp->next = peo[a];
	peo[a] = tmp;
}


long fangan[1002];


int main()
{
	freopen("news.in","r",stdin);
	freopen("news.out","w",stdout);
	scanf("%ld",&n);
 	for (long i=2;i<n+1;i++)
	{
		long a;
		scanf("%ld",&a);
		insert(a,i);
		insert(i,a);
	}
	long ans = oo;
	for (long i=1;i<n+1;i++)
	{
		for (long j=1;j<n+1;j++)
		{
			f[j] = -oo;
			s[j][0] = 0;
		}
		used[i] = true;
		dfs(i);
		used[i] = false;
		if (ans>f[i])
		{
			ans = f[i];
			fangan[0] = 1;
			fangan[1] = i;
		}
		else if (ans==f[i])
		{
			fangan[++fangan[0]] = i;
		}
	}
	printf("%ld\n",ans+1);
	for (long i=1;i<fangan[0]+1;i++)
		printf("%ld ",fangan[i]);
}


覃禹舜,节约空间,选择排序

program news;
type pnode=^node;
     node=record
           data:longint;
           next:pnode;
          end;
var
 mark:array[0..1001] of boolean;
 a,f,time:array[0..1001] of longint;
 p:array[0..1001] of pnode;
 max,len1,len2,t1,ans,n:longint;


procedure init;
begin
 assign(input,'news.in');reset(input);
 assign(output,'news.out');rewrite(output);
end;


procedure insert(x,y:longint);
var
 point:pnode;
begin
 new(point);
 point^.data:=y;
 point^.next:=p[x];
 p[x]:=point;
end;


procedure readdata;
var
 i,x:longint;
begin
 fillchar(p,sizeof(p),0);
 read(n);
 for i:=2 to n do
  begin
   read(x);
   insert(x,i); insert(i,x);
  end;
end;


function work(x,last:longint):longint;
var
 y:pnode;
 flag:boolean;
begin
 y:=p[x]; flag:=false;
 while y<>nil do
  begin
   if y^.data<>last then
    begin
     flag:=true;
     work(y^.data,x);
    end;
   y:=y^.next;
  end;
 if not flag then begin f[x]:=0; exit; end;
 y:=p[x];
 a[0]:=0;
 while y<>nil do
  begin
   if y^.data<>last then begin inc(a[0]); a[a[0]]:=f[y^.data] end;
   y:=y^.next;
  end;
 for len1:=2 to a[0] do
  for len2:=1 to len1-1 do
   if a[len2]<a[len1] then begin t1:=a[len2]; a[len2]:=a[len1]; a[len1]:=t1; end;
 for len1:=1 to a[0] do inc(a[len1],len1);
 max:=0;
 for len1:=1 to a[0] do
  if a[len1]>max then max:=a[len1];
 f[x]:=max;
end;


procedure main;
var
 i:longint;
begin
 for i:=1 to n do
  begin
   work(i,0);
   time[i]:=f[i];
  end;
 ans:=maxlongint;
 for i:=1 to n do if time[i]<ans then ans:=time[i];
 writeln(ans+1);
 for i:=1 to n do
  if time[i]=ans then write(i,' ');
end;


procedure terminate;
begin
 close(input);close(output);
 halt;
end;


begin
 init;
 readdata;
 main;
 terminate;
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值