【备战NOIP2012图论专项模拟试题】砍树 纪中3079 dfs

17 篇文章 0 订阅
4 篇文章 0 订阅

Description

给出一个树形图(“tree-shaped” network),有N(1 <= N <= 10,000)个顶点。如果删除树上某一个顶点,整棵树就会分割成若干个部分。显然,每个部分内部仍保持连通性。

现在问:删除哪个点,使得分割开的每个连通子图中点的数量不超过N/2。如果有很多这样的点,就按升序输出。

Input

第1行:1个整数N,表示顶点数。顶点编号1~N

第2..N行:每行2个整数X和Y,表示顶点X与Y之间有一条边

Output

若干行,每行1个整数,表示一个符合条件的顶点的编号。如果没有顶点符合条件,则仅在第1行上输出”NONE”

分析

首先读入数据,并构图。 
然后就是标准的DFS。返回以i为根的子树的结点总数。
当删除结点i时,整棵树被分成了i的父结点以上的部分,以及i以下的每棵子树部分。根据题意,每个部分的结点个数都不能超过n/2。 
每棵子树返回的结点数可以在递归时得到,每枚举一个子结点时,判断一下返回值就行。
对于i的父结点以上的部分,其实也很简单,就是总结点数减去i为根的总结点数。
当i符合条件后,把它压入一个数组ans。递归结束后,对ans排序、输出。

代码

const
  maxn=20000;
  maxe=50000;

type
  arr=record
    w:boolean;
    x,y:longint;
    next:longint;
end;

var
  edge:array[1..maxe] of arr;
  ls:array[1..maxn] of longint;
  number:array[1..maxn] of longint;
  n,m:longint;

procedure add(x,y:longint);
begin
  m:=m+1;
  edge[m].x:=x;
  edge[m].y:=y;
  edge[m].next:=ls[x];
  ls[x]:=m;
end;

procedure init;
var
  i,j,k:longint;
begin
  readln(n);
  for i:=1 to n-1 do
    begin
      readln(j,k);
      add(j,k);
      add(k,j);
    end;
end;

function tree(x,root:longint):longint;
var
  i,j,k:longint;
begin
  i:=ls[root];
  while i<>0 do
    begin
      if edge[i].y=x
        then
          begin
            edge[i].w:=true;
            i:=edge[i].next;
            continue;
          end;
      number[root]:=tree(root,edge[i].y)+number[root];
      i:=edge[i].next;
    end;
  inc(number[root]);
  tree:=number[root];
end;

procedure main;
var
  i,j,k:longint;
  flag,flag2:boolean;
begin
  init;
  tree(0,1);
  flag2:=false;
  for i:=1 to n do
    begin
      j:=ls[i];
      flag:=false;
      while j<>0 do
        with edge[j] do
          begin
            if w
              then
                begin
                  j:=next;
                  continue;
                end;
            if number[y]>n div 2
              then begin
                flag:=true;
                break;
              end;
            j:=next;
          end;
      if number[1]-number[i]>n div 2
        then flag:=true;
      if not flag
        then begin
          writeln(i);
          flag2:=true;
        end;
    end;
  if not flag2
    then write('NONE');
end;

begin
  main;
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值