1. 题目 信号放大器
【问题描述】
树型网络是最节省材料的网络。所谓树型网络,是指一个无环的连通网络,网络中任意两个结点间有且仅有一条通信道路。
网络中有一个结点是服务器,负责将信号直接或间接地发送到各终端机。如图6-4,server结点发出一个信号给结点a和c,a再转发给b。如此,整个网络都收到这个信号了。
server a b
●────○────○
│
│
○
但是,实际操作中,信号从一个结点发到另一个结点,会出现信号强度的衰减。衰减量一般由线路长度决定。 server 3 a 2 b
●────○────○
│
│1
○
如上图,边上所标的数字为边的衰减量。假设从server出发一个强度为4个单位的信号,发到结点a后强度衰减为4-3=1个单位。结点a再将其转发给结点b。由于信号强度为1,衰减量为2,因此信号无法发送到b。
一个解决这一问题的方法是,安装信号放大器。信号放大器的作用是将强度大于零的信号还原成初始强度(从服务器出发时的强度)。
上图中,若在结点a处安装一个信号放大器,则强度为4的信号发到a处,即被放大至4。这样,信号就可以被发送的网络中的任意一个节点了。为了简化问题,我们假定每个结点只处理一次信号,当它第二次收到某个信号时,就忽略此信号。
你的任务是根据给出的树型网络,计算出最少需要安装的信号放大器数量。
【输入】
第一行一个整数n,表示网络中结点的数量。(n<=100000)
第2~n+1行,每行描述一个节点的连接关系。其中第i+1行,描述的是结点i的连接关系:首先一个整数k,表示与结点i相连的结点的数量。此后2k个数,每两个描述一个与结点i相连的结点,分别表示结点的编号(编号在1~n之间)和该结点与结点i之间的边的信号衰减量。结点1表示服务器。
最后一行,一个整数,表示从服务器上出发信号的强度。
【输出】
一个整数,表示要使信号能够传遍整个网络,需要安装的最少的信号放大器数量。
如果不论如何安装信号放大器,都无法使信号传遍整个网络,则输出“No solution.”
【样例】
booster.in booster.out
4 1
2 2 3 3 1
2 1 3 4 2
1 1 1
1 2 2
4
2. 题目实质
从一个点中正向发送信号使其可以传送到任何一个点且代价最小,不能传送就输出特殊标记。
3. 算法
首先,当有某个节点不与任何节点连通(貌似这种情况在数据中不存在)时,以及有某两个节点之间的传输线路的代价值大于最大代价值时,输出特殊标记。
这道题此处主要讲两种算法:
我们可以从叶结点往上推,设叶结点收到的信号大小为1(表示至少需要强弱为1的信号),如下图:
1 |
1 |
3 |
1 |
接下来,往上观察结点a,它有一个子结点b和一个子结点d,两个子结点均需要保证有1的强度,而路上线路长2和1,所以从结点a法相两个子结点的响度保证值应该为2+1=3和1+1=2,取其中较大的一个,所以结点a应该返回“至少需要保证强弱为3”。
最后分析道server,两条线路保证值分别为1+1=2,3+3=6,由于6>最大信号强度,所以在a的位置必须安装一个扩大器。
综上所述,也就是在每个结点,将它的子结点综合,求出最大的响度,如果其中有一个响度大于最大响度,那么必须在那个结点的位置安装放大器。
这个贪心算法为什么起作用呢?为什么在结点a处安装,而不在a的子结点处如b安装呢?很明显,由于信号只能从根往叶传递,那么假设这时可以在a、b、d处安装,那么取他们中辈分最大的一定最优,因为同样满足那一条支(就是图中的a-b那一条),而辈分较大的可以供更多的结点使用。比如说上图,如果响度为4,那么就不能在b安装了。
这样贪心算法基本建立。
再者,这个题也可以直接搜索。从根节点开始依次往下传输,记录途中加的信号放大器的数量,然后依次遍历每一条路径,记录它的最小值。
但是,鉴于这次考查的是树方面的知识,而且这里对于树的建立也有人问到所以再讲一下如何建立起这棵树。
首先,由题意得,信号最早是由一号节点发出的,所以这里不妨设一号节点为根节点,然后依次根据读入的连接情况建立起这棵树注意这里的数据中所描述的连接情况中,把这个节点和他的父亲节点的连接情况也囊括了进去,所以本人的算法是用父亲儿子表示法,每次读入一种新的连接情况时,都判断一下这个节点是不是他的父亲节点,这样构建起来的树是多叉树可以转一下二叉树,也可以就拿多叉树算。
但是,又一位大牛的方法很大牛,因为 1 号节点是根节点,然后依次往下插入他的子节点所以一个点的子节点一定比他本身的编号要大,直接判断新读入的关系中的节点是否比该节点要大即可,省去了调用一次链表的麻烦。
4. 注意事项
此处的树的构建比较需要注意链表用不用模拟都可以。
当然,如果干脆用图的邻接表存自然也米有问题,因为树不过是一种特殊的图。
5. 时空复杂度
搜索 ???
贪心 O(n)???
6. 程序代码
链表 + 搜索
type
pointer=^node;
node=record
data,weight:longint;
next:pointer;
useful:boolean;
end;
arr=array[0..1] of longint;
var
ge:array[1..100000] of pointer;
visited:array[1..100000] of boolean;
s,n,ans:longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a) else exit(b);
end;
procedure make_tree(i:longint);
var
p:pointer;
begin
visited[i]:=true;
p:=ge[i];
while p<>nil do
begin
if not visited[p^.data] then
make_tree(p^.data)
else
p^.useful:=false;
p:=p^.next;
end;
end;
function cal(i,m:longint):arr;
var
p:pointer;
tmp,re:arr;
begin
p:=ge[i];
re[0]:=0;
re[1]:=1;
while p<>nil do
begin
if p^.useful then
begin
if (m-p^.weight>=1) and (re[0]<maxlongint) then
begin
tmp:=cal(p^.data,m-p^.weight);
re[0]:=re[0]+min(tmp[0],tmp[1]);
end
else
re[0]:=maxlongint;
tmp:=cal(p^.data,s-p^.weight);
re[1]:=re[1]+min(tmp[0],tmp[1]);
end;
p:=p^.next;
end;
cal:=re;
end;
procedure init;
var
i,j,v,w,k:longint;
p:pointer;
begin
// assign(input,'booster.in');
reset(input);
readln(n);
for i:=1 to n do
ge[i]:=nil;
for i:=1 to n do
begin
read(k);
for j:=1 to k do
begin
read(v,w);
new(p);
p^.data:=v;
p^.weight:=w;
p^.useful:=true;
p^.next:=ge[i];
ge[i]:=p;
end;
end;
readln(s);
close(input);
end;
procedure work;
var
p: pointer;
tmp: arr;
begin
fillchar(visited,sizeof(visited),false);
make_tree(1);
p:=ge[1];
ans:=0;
while p<>nil do
begin
if p^.useful then
begin
tmp:=cal(p^.data,s-p^.weight);
ans:=ans+min(tmp[0],tmp[1]);
end;
p:=p^.next;
end;
end;
procedure print;
begin
// assign(output,'booster.out');
rewrite(output);
writeln(ans);
close(output);
end;
begin
init;
work;
print;
end.
模拟链表 + 搜索
type
stu = record
fa, n, len: longint;
son: array[1..1000] of longint;
end;
var
f, f1: text;
n, k, i, j, sum, min: longint;
a: array[1..100000] of stu;
s: array[1..100000] of integer;
procedure init;
var x, y: longint;
begin
// assign(f,'booster.in');
reset(f);
// assign(f1,'booster.out');
rewrite(f1);
readln(f,n);
for i:=1 to n do
begin
read(f,k);
a[i].n:=0;
for j:=1 to k do
begin
read(f,x,y);
if x>i then
begin
inc(a[i].n);
a[i].son[a[i].n]:=x;
a[x].fa:=i;
a[x].len:=y;
end;
end;
end;
readln(f,min);
fillchar(s,sizeof(s),0);
sum:=0;
end;
procedure clo;
begin
writeln(f1,sum);
close(f);
close(f1);
end;
procedure try(i: integer);
var j: integer;
begin
if a[i].n=0 then
s[i]:=0
else
begin
for j:=1 to a[i].n do
begin
try(a[i].son[j]);
if s[a[i].son[j]]+a[a[i].son[j]].len>s[i] then
s[i]:=s[a[i].son[j]]+a[a[i].son[j]].len;
end;
if s[i]>min then
begin
inc(sum);
s[i]:=1;
end;
end;
end;
begin {main}
init;
try(1);
clo;
end.
链表 + 贪心
Const
Inf = 'Booster.in';
Ouf = 'Booster.out';
MaxN = 100000;
Type
TEg = Record
Fi,Long : Longint;
End;
TList = Array [1..MaxN] Of TEg;
TMap = Array [1..MaxN] Of Record {动态空间邻接表来储存边}
Num : Longint;
List : ^TList;
End;
THash = Array [1..MaxN] Of Boolean;
Var
n,m,s : Longint;
Map : TMap;
Procedure Init;
Var
Fin : Text;
i,j : Longint;
Begin
// Assign(Fin,Inf);
Reset(Fin);
Readln(Fin,n);
For i:=1 to n Do
Begin
Read(Fin,Map[i].Num);
Getmem(Map[i].List,Sizeof(TEg)*Map[i].Num); {动态分配邻接表的空间}
For j:=1 to Map[i].Num Do
Read(Fin,Map[i].List^[j].Fi,Map[i].List^[j].Long);
Readln(Fin);
End;
Readln(Fin,m);
Close(Fin);
End;
Procedure Doit;
Var
Fou : Text;
Hash : THash;
i,j,
Ans : Longint;
Procedure NoAnswer;
Begin
Writeln(Fou,'No solution.');
Close(Fou);
Halt;
End;
Function Search(o:Longint):Longint; {返回结点o的响度保证值}
Var
F : Boolean;
i,j,
Max,Min : Longint;
Begin
F:=True;
For i:=1 to Map[o].Num Do
If Not Hash[Map[o].List^[i].Fi] Then Begin
F:=False;
Break;
End;
If F Then Exit(1); {如果是叶结点}
Hash[o]:=True; Max:=-1;
{ Min:=m+1;}
For i:=1 to Map[o].Num Do {处理它的所有子结点}
If Not Hash[Map[o].List^[i].Fi] Then Begin
j:=Search(Map[o].List^[i].Fi);
If j+Map[o].List^[i].Long>m Then Begin {必须安装扩大器}
j:=1;
Inc(Ans);
End;
If j+Map[o].List^[i].Long>Max Then
Max:=j+Map[o].List^[i].Long;
End;
Search:=Max;
End;
Begin
// Assign(Fou,Ouf);
Rewrite(Fou);
For i:=1 to n Do {预先将无解情况排除}
For j:=1 to Map[i].Num Do
If Map[i].List^[j].Long>=m Then NoAnswer;
Fillchar(Hash,Sizeof(Hash),False);
Hash[1]:=True;
Ans:=0;
Search(1);
Writeln(Fou,Ans);
Close(Fou);
End;
Begin
Init;
Doit;
End.
模拟链表 + 贪心 (话说就这个代码短,汗)
type
point=record
data,father,need:integer;
put:boolean;
end;
var
a:array[1..100000] of point;
n,i,x,j,p,q,max:integer;
tot:integer;
begin
readln(n);
for i:=1 to n do
begin
read(x);
a[i].need:=0;
a[i].put:=false;
for j:=1 to x do
begin
read(p,q);
if p>i then
begin
a[p].father:=i;
a[p].data:=q;
end;
end;
end;
readln(max);
for i:=n downto 2 do
begin
x:=a[i].need+a[i].data;
if x>max then
begin
if a[x].put=false then tot:=tot+1;
a[x].put:=true;
x:=a[i].data;
end;
writeln(x);
if x>a[a[i].father].need then a[a[i].father].need:=x;
end;
writeln(tot);
end.
这个题的解题报告网上相当少见,所以我来写一份。