树形dp 二叉树版本与多叉树版本

来源:互联网 发布:金蝶网络破解版 编辑:程序博客网 时间:2024/06/05 00:09

以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;}










0 0