TopCoder[TCO2016 Round 1A]:EllysTree(1000)
|
题意:给定一棵有根数,标号为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 };