以51nod的 1405代码为例。
简单的树形dp其时间复杂度与空间复杂度都跟节点有关,其复杂度可以达到O(n),是线性的复杂度
拓扑排序复杂度也是。
#include<iostream>
using namespace std;
#define maxlen 100001
#define maxlen1 101
//int s[maxlen];
int c[maxlen][5];
int myarray[maxlen];
//int father[maxlen];
bool is_visited[maxlen];
long long result[maxlen];
int tree_size[maxlen];
//int ans[maxlen][maxlen1][3];
int n, root;
void getInput()
{
scanf("%d", &n);
int x, y;
for (int i = 1; i < n; ++i)
{
scanf("%d%d", &x, &y);
c[x][++c[x][0]] = y;
c[y][++c[y][0]] = x;
}
/* for (int i = 1; i <= n; ++i)
{
printf("%d\t", c[i][0]);
}*/
}
int max(int x, int y)
{
if (x < y)
{
return y;
}
return x;
}
void dp(int x)
{
// int num = c[x][0] - 1, left_child, right_child;
tree_size[x] = 1;
for (int i = 1; i < c[x][0]; ++i)
{
result[x] += result[c[x][i]] + tree_size[c[x][i]];
tree_size[x] += tree_size[c[x][i]];
}
//cout << root << '\t' << x << '\t' <<c[x][0]<<'\t'<< result[x] <<'\t'<<tree_size[x]<<"fdjj大将军"<< endl;
}
//建立树,时间复杂度O(n),n为树节点个数。
//myarray数组存储的是拓扑排序过程的节点序列,可以后续利用
//若是根节点可能为0或负数,要用另一种写法
void buildTree()
{
//找根root的过程,可以自己设定
int num = 0, index_begin = 0;
for (int i = 1; i <= n; ++i)
{
if (1 == c[i][0])
{
myarray[num++] = i;
is_visited[i] = true;
//cout << "vistit" << i << endl;
}
else if (2 == c[i][0])
{
root = i;
}
}
if (0 == root)
{
root = myarray[index_begin++];
is_visited[root] = false;
}
++c[root][0];
int backup_index_begin = index_begin;
// cout << "根"<<root << endl;
//利用叶子节点度为1的思想,进行拓扑排序,建立树,最终c[i][0]存储 度、边数, 1-c[i][0]为所有相邻点, 最后一个c[i][c[i][0]]为父节点
//根节点的父节点为0,也可以自己设定。
int index, tmp_father, tmp, father_index;
while (index_begin < num)
{
index = myarray[index_begin++];
tmp = 1;
//cout << num << '\t' << index << '\t' << c[index][0] <<'\t'<<c[index][1]<<'\t'<<c[index][tmp]<< endl;
while (tmp_father = c[index][tmp++])
{
if (!is_visited[tmp_father])
{
//father[index] = tmp_father;
if (1 == (--c[tmp_father][0]))
{
myarray[num++] = tmp_father;
is_visited[tmp_father] = true;
//cout << "vistit" << tmp_father << endl;
}
father_index = tmp - 1;
while (c[index][tmp])
{
tmp++;
}
--tmp;
//cout << index << '\t' << father_index << '\t' << tmp << endl;
if (father_index != tmp)
{
c[index][father_index] = c[index][tmp];
c[index][tmp] = tmp_father;
}
c[index][0] = tmp++;
dp(index);
// cout << index << '\t' << "childnum" << c[index][0] <<'\t'<<tmp_father<<endl;
}
}
}
c[index][0] = --tmp;
c[index][tmp] = 0;
dp(index);
//从根节点开始递推
for (int i = num - 2; i >= backup_index_begin; --i)
{
index = myarray[i];
result[index] = result[c[index][c[index][0]]] + n - (tree_size[index]<<1);
}
}
void test()
{
for (int i = 1; i <= n; ++i)
{
printf("%lld\n", result[i]);
}
}
int main()
{
getInput();
buildTree();
test();
return 0;
}
多叉树一般版本:
#include<iostream>
using namespace std;
#define maxlen 100001
#define maxlen1 101
//int s[maxlen];
int *c[maxlen];//[5]
int x[maxlen], y[maxlen];
int myarray[maxlen];
//int father[maxlen];
bool is_visited[maxlen];
long long result[maxlen];
int tree_size[maxlen];
int friend_num[maxlen];
//int ans[maxlen][maxlen1][3];
int n, root;
void getInput()
{
scanf("%d", &n);
/* int x, y;
for (int i = 1; i < n; ++i)
{
scanf("%d%d", &x, &y);
c[x][++c[x][0]] = y;
c[y][++c[y][0]] = x;
}*/
for (int i = 1; i < n; ++i)
{
scanf("%d%d", &x[i], &y[i]);
++friend_num[x[i]];
++friend_num[y[i]];
}
for (int i = 1; i <= n; ++i)
{
c[i] = new int[friend_num[i] + 1];
c[i][0] = 0;
}
for (int i = 1; i < n; ++i)
{
c[x[i]][++c[x[i]][0]] = y[i];
c[y[i]][++c[y[i]][0]] = x[i];
}
/* for (int i = 1; i <= n; ++i)
{
printf("%d\t", c[i][0]);
}*/
}
int max(int x, int y)
{
if (x < y)
{
return y;
}
return x;
}
void dp(int x)
{
// int num = c[x][0] - 1, left_child, right_child;
tree_size[x] = 1;
for (int i = 1; i < c[x][0]; ++i)
{
result[x] += result[c[x][i]] + tree_size[c[x][i]];
tree_size[x] += tree_size[c[x][i]];
}
// cout << root << '\t' << x << '\t' <<c[x][0]<<'\t'<< result[x] <<'\t'<<tree_size[x]<<"fdjj大将军"<< endl;
}
//建立树,时间复杂度O(n),n为树节点个数。
//myarray数组存储的是拓扑排序过程的节点序列,可以后续利用
//若是根节点可能为0或负数,要用另一种写法
void buildTree()
{
const int father_num = 1, root_father = 0; //root_father也可以设为n+1
//找根root的过程,可以自己设定
root = 1;
int num = 0;
for (int i = 1; i <= n; ++i)
{
// cout << c[i][0] <<'\t';
if (1 == c[i][0])
{
myarray[num++] = i;
is_visited[i] = true;
//cout << "vistit" << i << endl;
}
else if (c[i][0] > c[root][0]) //把孩子最多的节点作为root
{
root = i;
}
}
/* if (0 == root)
{
root = myarray[--num];
is_visited[root] = false;
}*/
++friend_num[root];
c[root][++c[root][0]] = 0; //为了计算方便,将0作为根节点的父节点
// c[root][0] = c[root][0] * 2 + 1;
// int backup_index_begin = index_begin;
// cout << "根"<<root <<'\t'<<num<< endl;
//利用叶子节点度为1的思想,进行拓扑排序,建立树,最终c[i][0]存储 度、边数, 1-c[i][0]为所有相邻点, 最后一个c[i][c[i][0]]为父节点
//根节点的父节点为0,也可以自己设定。
int index, tmp_father, index_begin = 0;
while (index_begin < num)
{
index = myarray[index_begin++];
//cout << num << '\t' << index << '\t' << c[index][0] <<'\t'<<c[index][1]<<'\t'<<c[index][tmp]<< endl;
for (int i = 1; i <= c[index][0]; ++i)
{
if (!is_visited[tmp_father = c[index][i]])
{
if ((--friend_num[tmp_father]) == father_num) //删减index,若之后父节点入度为0,则入队列
{
myarray[num++] = tmp_father;
is_visited[tmp_father] = true;
// cout <<index<< "vistit" << tmp_father << endl;
}
c[index][i] = c[index][c[index][0]]; //father移至最后
c[index][c[index][0]] = tmp_father;
break;
}
}
dp(index);
}
//从根节点开始递推
for (int i = num - 2; i >= 0; --i)
{
index = myarray[i];
result[index] = result[c[index][c[index][0]]] + n - (tree_size[index]<<1);
}
}
void test()
{
for (int i = 1; i <= n; ++i)
{
printf("%lld\n", result[i]);
}
}
int main()
{
getInput();
buildTree();
test();
return 0;
}