树形dp入门题目总结——树的重心和最大独立集

来源:互联网 发布:喀秋莎录屏软件7 编辑:程序博客网 时间:2024/06/05 22:58

树形dp入门题目总结——树的重心和最大独立集

  • 树形dp入门题目总结树的重心和最大独立集
    • 一 无根树转为有根树
      • 工人的请愿书
    • 二 树的重心
      • Balancing Act
    • 三 树的最大独立集
      • Anniversary party
      • Party at Hali-Bula

一. 无根树转为有根树

输入n个节点的无根树的各条边,指定一个节点为根,将无根树转为有根树。

int fa[maxn];   //每个节点的父亲 vector<int> G[maxn];int dfs(int f)      //无根树化为有根树 {    int i;    for(i=0; i<G[f].size(); i++)    {        if(fa[f] != G[f][i]){            fa[G[f][i]] = f;             dfs(G[f][i]);        }    }}

初始时设置fa[1] = -1,调用dfs(1)即可。

工人的请愿书

uva12186 题目链接
一个老板,n个员工,老板编号为0,除了老板之外每个工人都有唯一的直属上司,现在工人要签署一项请愿书,每个工人只能交给其直属上司,上司的直属下属中不少于T%的人签字,他才会向上提交,问要让老板收到,至少需要多少工人签字。
思路:
d[i]表示i员工上交时最少需要多少人签字,访问i节点时,将其子节点值排序取前面T%人相加。

#include<iostream>#include<algorithm>#include<vector> #include<cmath>using namespace std;const int maxn = 100001;int n, t;vector<int> G[maxn];int d[maxn];    //d[i]表示i员工上交时最少需要多少人签字 void dfs(int k){    int i;    vector<int> ans;    for(i=0; i<G[k].size(); i++)    {        int v = G[k][i];        dfs(v);        ans.push_back(d[v]);    }    d[k] = 0;    if(G[k].size() == 0){        d[k] = 1;        return;    }    sort(ans.begin(), ans.end());    for(i=0; i<ceil((double)t/100*G[k].size()); i++)    {        d[k] += ans[i];    }}int main(){    int i, j, k;    while(1)    {        cin >> n >> t;        if(n == 0 && t == 0){            break;        }        for(i=0; i<=n; i++){            G[i].clear();        }        for(i=1; i<=n; i++)        {            int x;            cin >> x;            G[x].push_back(i);        }        dfs(0);        cout << d[0] << endl;    }    return 0; } 

二. 树的重心

对于一个无根树,找到一个节点,使以这个节点为根时其他子树的最大节点数最小。

Balancing Act

POJ1655 题目链接
Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1…N. Deleting any node from the tree yields a forest: a collection of one or more trees. Define the balance of a node to be the size of the largest tree in the forest T created by deleting that node from T.

Deleting node 4 yields two trees whose member nodes are {5} and {1,2,3,6,7}. The larger of these two trees has five nodes, thus the balance of node 4 is five. Deleting node 1 yields a forest of three trees of equal size: {2,6}, {3,7}, and {4,5}. Each of these trees has two nodes, so the balance of node 1 is two.

For each input tree, calculate the node that has the minimum balance. If multiple nodes have equal balance, output the one with the lowest number.

思路:
在无根树转为有根树过程中,对每个节点k求其以k为根的各个子树中的最大节点数maxx,和k这颗子树节点数总和num,k的实际子树的最大值为max(maxx, n - num);

#include<iostream>#include<vector>#include<algorithm>using namespace std;const int maxn = 20001;vector<int> G[maxn];int fa[maxn];   //每个节点的父亲 int node[maxn]; //每个节点的最大子树节点数 int n;int dfs(int f)      //无根树化为有根树 {    int i;    int maxx = 0;    int count = 1;    for(i=0; i<G[f].size(); i++)    {        if(fa[f] != G[f][i]){            fa[G[f][i]] = f;             int cur = dfs(G[f][i]);            count += cur;            if(cur > maxx){                maxx = cur;            }        }    }    node[f] = max(maxx, n - count);    return count;}int main(){    int i, j, k;    int t;    cin >> t;    for(k=1; k<=t; k++)    {        cin >> n;        for(i=1; i<=n; i++){            G[i].clear();            fa[i] = 0;        }        for(i=1; i<n; i++)        {            int a, b;            cin >> a >> b;            G[a].push_back(b);            G[b].push_back(a);        }        dfs(1);        int minn = 999999999;        int mini;        for(i=1; i<=n; i++){            if(node[i] < minn){                minn = node[i];                mini = i;            }        }        cout << mini << " " << minn << endl;    }}

三. 树的最大独立集

对于一个n个节点的无根树,选出尽量多的节点,使得任意两个节点不相邻。

Anniversary party

hdu1520 题目链接
每个节点有权值,问选中的最大权值为多少

思路:
定义dp[i][0]为不选择i节点的最大权值,d[i][1]为选中i节点的最大权值。
状态转移方程:dp[i][0] = max(dp[j][0], dp[j][1])
dp[i][1] = dp[j][0] + value[i].

#include<stdio.h>#include<cstring>#include<vector>#include<iostream>using namespace std;int n;vector<int> v[6001];int dp[6001][2];int visit[6001];void dpp(int x){    if(visit[x] == 1){        return;    }    visit[x] = 1;    int i;    for(i=0; i<v[x].size(); i++)    {        dpp(v[x][i]);        int t = v[x][i];        dp[x][0] += max(dp[t][0], dp[t][1]);        dp[x][1] += dp[t][0];    }}int main(){    int n;    int i;    while(~scanf("%d", &n))    {        for(i=0; i<6001; i++){            v[i].clear();        }        memset(visit, 0, sizeof(visit));        memset(dp, 0, sizeof(dp));        for(i=1; i<=n; i++)        {            scanf("%d", &dp[i][1]);        }        while(1)        {            int a, b;            scanf("%d%d", &a, &b);            if(a == 0 && b == 0){                break;            }            v[b].push_back(a);        }        int maxx = 0;        for(i=1; i<=n; i++)        {            dpp(i);            if(maxx < dp[i][0]){                maxx = dp[i][0];            }            if(maxx < dp[i][1]){                maxx = dp[i][1];            }        }        printf("%d\n", maxx);    }    return 0;}

Party at Hali-Bula

uva1220 题目链接
问最多选择多少人,且方案是否唯一
思路:
前一问相当于最大独立集问题,后一问判断唯一性
定义d[i][0]表示i节点不选最多选多少人,d[i][1]表示i节点选择最多选多少人 ,
f[i][0]表示i节点不选的方案是否唯一,f[i][1]表示i节点选择方案是否唯一 .
d数组的状态转移方程和前一题类似。
f[i][1] = 1当且仅当f[j][0] = 1,j节点为i节点子节点中任意一点,表示唯一,
f[i][0]时,每个子节点可选可不选,若选择和不选择的d值一样,则f[i][0]=0,表示不唯一,否则每个子节点最大d值得情况下,其f值来决定f[i][0]值,具体参考程序代码。

#include<iostream>#include<vector>#include<map>#include<string>using namespace std;const int maxn = 201;map<string, int> names;vector<int> G[maxn];int d[maxn][2];  //d[i][0]表示i节点不选最多选多少人,d[i][1]表示i节点选择最多选多少人 int f[maxn][2];  //f[i][0]表示i节点不选的方案是否唯一,f[i][1]表示i节点选择方案是否唯一 int n;void dfs(int k){    int i;    d[k][1] = 1;    f[k][1] = 1;    d[k][0] = 0;    f[k][0] = 1;    for(i=0; i<G[k].size(); i++)    {        int v = G[k][i];        dfs(v);        d[k][1] += d[v][0];        if(!f[v][0]){            f[k][1] = 0;        }        if(d[v][0] < d[v][1]){            d[k][0] += d[v][1];            if(!f[v][1]){                f[k][0] = 0;            }        }        else{            d[k][0] += d[v][0];            if(!f[v][0]){                f[k][0] = 0;            }            if(d[v][0] == d[v][1]){                f[k][0] = 0;            }        }    }   }int main(){    int i, j, k;    //freopen("tt.out", "w", stdout);    while(1)    {        cin >> n;        if(n == 0){            break;        }        for(i=0; i<=n; i++){            G[i].clear();        }        i = 1;        string str;        cin >> str;        names[str] = i;        string str1[201][2];        for(i=2; i<=n; i++)        {            cin >> str1[i][0] >> str1[i][1];            names[str1[i][0]] = i;        }        for(i=2; i<=n; i++){            int u = names[str1[i][1]];            int v = names[str1[i][0]];            G[u].push_back(v);        }        dfs(1);        if(d[1][0] < d[1][1]){            cout << d[1][1] << " ";            cout << (f[1][1] ? "Yes" : "No") << endl;        }        else{            cout << d[1][0] << " ";            if(d[1][0] == d[1][1]){                cout << "No" << endl;            }            else{                cout << (f[1][0] ? "Yes" : "No") << endl;            }        }    }    return 0;}
0 0