TopCoder[TCO2016 Round 1A]:EllysTree(1000)

本文解析了一个关于在有根树中进行跳跃访问的问题。要求从根节点出发,通过向上或向下跳跃的方式,访问所有节点恰好一次,并使得访问序列字典序最小。文章详细介绍了问题背景及解决方案。

Problem Statement

    Elly has a graph with N+1 vertices, conveniently numbered from 0 to N. The graph is actually a rooted tree, with the root being the vertex with number zero. 



Elly can move between the vertices of her tree by jumping from one vertex to another. Not all jumps are allowed. Elly may jump from vertex A to vertex B if and only if one of A and B is a (direct or indirect) descendant of the other. 



Elly is currently standing in the root of the tree: vertex 0. She would like to make a sequence of N jumps that visits each of the other N vertices exactly once. Note that Elly is allowed to jump over previously visited vertices. For example, if A is an ancestor of B and B is an ancestor of C, Elly can jump from A to C or from C to A even if B has been already visited. 



You are given the description of the tree: a vector <int> parent with N elements. For each i between 0 and N-1, inclusive, the vertex parent[i] is the parent of the vertex (i+1). If it is possible for Elly to visit each of the vertices 1 through N exactly once, return a vector <int> with N elements: the numbers of the vertices in the order in which she should visit them. If there is more than one possible answer, return the lexicographically smallest one. If there is no way to achieve her goal, return an empty vector <int> instead.

Definition

    
Class:EllysTree
Method:getMoves
Parameters:vector <int>
Returns:vector <int>
Method signature:vector <int> getMoves(vector <int> parent)
(be sure your method is public)

Limits

    
Time limit (s):2.000
Memory limit (MB):256

Notes

-A tree is a connected graph with N+1 vertices and N edges. A rooted tree is a tree in which one vertex is labeled as the root.
-In a rooted tree, the parent of vertex X is the first vertex on the path from X to the root. The root has no parent.
-In a rooted tree, vertex X is a descendant of vertex Y if Y lies on the path from X to the root.
-Given two equally long but different sequences of integers A and B, A is said to be lexicographically smaller than B if A contains a smaller number on the first position where they differ.

Constraints

-parent will contain between 1 and 100 elements, inclusive.
-Each element of parent will be between 0 and |parent|, inclusive, where "|parent|" denotes the number of elements in parent (i.e. N).
-It is guaranteed, that the given graph will be a valid rooted tree.

Examples

0) 
    
{9, 13, 7, 9, 8, 14, 14, 0, 6, 9, 2, 2, 5, 5, 7}
Returns: {1, 5, 2, 11, 13, 12, 8, 3, 7, 15, 14, 4, 6, 9, 10 }
The nodes Elly can jump to from node 6 are: {0, 8, 5, 14, 9, 10, 1, 4}.
1) 
    
{3, 4, 5, 0, 2}
Returns: {1, 2, 3, 4, 5 }
There are no branches in this tree, thus Elly can traverse it in any order.
2) 
    
{0, 0}
Returns: { }
The root has two children. No matter which of them Elly chooses first, she will not be able to get to the other, since the girl has to go back to the root, which is already visited.
3) 
    
{0, 6, 6, 2, 6, 1, 3, 5}
Returns: {2, 4, 1, 3, 7, 6, 5, 8 }
 

题意:给定一棵有根数,标号为0~n,你可以从根(0号点)开始,在这棵树上跳。你只能在子孙与祖先跳跃。要求跳跃n次后访问每个点各一次,并使访问序列字典序最小。

 

题解:

首先想到的是如何构造出一个可行的方案。我们把跳跃分为向上跳(深度减少)和向下跳 (深度增加)两种。显然,叶节点只可以通过向下跳来访问,访问后也只能向上跳。对于一个非根的非叶节点,其叶节点一定会被访问,我们可以使所有的非根的非叶节点都通过向上跳来访问。

这样,构造一个可行序列的方法就是:先从根跳到一个叶节点上,在逐渐向上跳,直到跳到一个子树没有都被访问的节点。然后再跳到其子树上的一个未被访问的叶节点(一定存在),重复操作。

这样的过程可以看做是用非叶节点去消掉其下方的叶节点,可以用DP处理。

因为答案要求字典序最小,我们可以每次枚举跳到哪个点上,再DP验证之后是否存在可行方案。注意DP时应忽略被访问过的点,对于当前位于的点要特殊考虑。

步数复杂度为n,枚举复杂度为n,DP复杂度为n,总复杂度O(n^3)。

 

代码:

 1 int a[200],b[200],c[200],dp[200],v[200],dp2[200],dep[200],siz[200],now,n;
 2 void qq(int x,int fa)
 3 {
 4     dep[x]=dep[fa]+1;
 5     for(int i=c[x];i;i=b[i])qq(i,x);
 6 }
 7 bool gcd(int x,int y)
 8 {
 9     if(dep[x]>dep[y])swap(x,y);
10     while(dep[x]<dep[y])y=a[y];
11     return x!=y;
12 }
13 void ss(int x)
14 {
15     dp[x]=0; dp2[x]=0; siz[x]=0;
16     for(int i=c[x];i;i=b[i])
17     {
18         ss(i); dp[x]=dp[x]+dp[i]; dp2[x]=max(dp2[x],dp2[i]); siz[x]=siz[x]+siz[i];
19     }
20     if((v[x]==0)and(siz[x]==0))dp[x]++; if(now==x)dp2[x]=1;
21     if((dp[x]>0)and(siz[x]!=0)and((v[x]==0)or(now==x)))
22     {
23         if(dp2[x]==1)dp[x]=max(dp[x]-1,0);else dp[x]=max(dp[x]-1,1);
24     }
25     if((v[x]==0)or(x==now))siz[x]++;
26 }
27 class EllysTree
28 {
29     public:
30     vector <int> getMoves(vector <int> parent)
31     {
32         //$CARETPOSITION$
33         vector <int> ans; n=parent.size();
34         for(int i=0;i<n;i++)a[i+1]=parent[i];
35         for(int i=1;i<=n;i++){ b[i]=c[a[i]]; c[a[i]]=i; }
36         qq(0,0);
37         now=0; v[0]=1; ss(0); if(dp[0]>0)return ans; 
38         for(int ii=1;ii<=n;ii++)
39         {
40             for(int i=1;i<=n;i++)
41             if(v[i]==0)
42             {
43                 if(gcd(now,i))continue;
44                 int tnow=now; now=i; v[i]=1; ss(0); if(dp[0]==0){ ans.push_back(i); break; }
45                 now=tnow; v[i]=0;
46             }
47         }
48         return ans;
49     }
50 };
View Code

转载于:https://www.cnblogs.com/GhostReach/p/6682213.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值