Description
现在有一棵树T,有N个节点,我们想通过去掉一个节点p来把T分割成更小的树,并且满足每个小树中的节点数不超过n/2。
请根据输入的树来输出所有可能的p的号码。
Input Format
第1行:一个整数N,代表有N个节点,且每个节点的编号为1,2,...,N。
第2~N行:每行两个整数x,y,代表节点x和节点y之间连通。
Output Format
从小到大一次输出满足条件的p的号码,每行1个可行解。
Input Sample
10
1 2
2 3
3 4
4 5
6 7
7 8
8 9
9 10
3 8
Output Sample
3 8
想法其实很简单...
首先用邻接表存储整个图(注意, n个点 n-1个边的图除了二叉树有很多种可能.....)
然后 把每个点的每个分支的节点数计算出来 最后进行判断输出即可
//递归的描述很简单
遍历所有的点,不断的递归调用getComponent函数即可,指定源头和分支首元素。
代码如下(没用Vector):
#include <iostream> #define MaxN 10000+10 using namespace std; int n; int connect[MaxN][200]={0}; int len_c[MaxN]={0}; int seg[MaxN][200]={0}; bool caled[MaxN] = {0}; void init(){ cin>>n; //记录连通情况 O(n) for (int i = 0; i < n-1; ++i) { int x,y; cin>>x>>y; connect[x][len_c[x]++] = y; connect[y][len_c[y]++] = x; } //初始化所有只有一个分支的点的分割情况 for (int i = 1; i <= n; ++i) if(len_c[i]==1) { seg[i][0] = n-1; caled[i] = true; } //初始化有两个分支 且至少有一个分支只有一个元素的节点的分割情况 for (int i = 1; i <= n; ++i) if(len_c[i]==2) { if(len_c[connect[i][0]]==1){ seg[i][0] = 1; seg[i][1] = n-2; caled[i] = true; } if(len_c[connect[i][1]]==1){ seg[i][1] = 1; seg[i][0] = n-2; caled[i] = true; } } // } //计算与s相连的 以x开头的那一个分支有多少个节点 int getComponent(int s, int x){ int len = len_c[x]; int res = 1;//肯定有x本身 if(caled[x]){ for (int i = 0; i < len; ++i) if(connect[x][i]!=s) res += seg[x][i]; }else{//说明x的seg情况没有初始化过 int tmp = 0; for (int i = 0; i < len-1; ++i) { seg[x][i] = getComponent(x,connect[x][i]); tmp += seg[x][i]; if(connect[x][i]!=s) res += seg[x][i]; } seg[x][len-1] = n-1-tmp; caled[x] = true; } return res; } void Build(){ //去构建所有的seg for (int i = 1; i <= n; ++i) if(!caled[i]) { int len = len_c[i]; int tmp = 0; for (int j = 0; j < len-1; ++j){ //递归计算 i的以j开头的那个分支有多少个元素 seg[i][j] = getComponent(i,connect[i][j]); tmp += seg[i][j]; } seg[i][len-1] = n-1-tmp;//最后一个分支可以用补集的思想 caled[i] = true; } } void Output(){ for (int i = 1; i <= n; ++i) { bool ok = true; for (int j = 0; j < 3; ++j) if(seg[i][j]>n/2) { ok = false; break; } if(ok) cout<<i<<endl; } } int main(int argc, char const *argv[]) { init(); Build(); Output(); return 0; }