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.