算法训练 结点选择

题目:

时间限制:1.0s   内存限制:256.0MB
   
问题描述

有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
输入格式

第一行包含一个整数 n 。

接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。

接下来一共 n-1 行,每行描述树上的一条边。
输出格式
输出一个整数,代表选出的点的权值和的最大值。
样例输入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
样例输出
12
样例说明
选择3、4、5号点,权值和为 3+4+5 = 12 。
数据规模与约定

对于20%的数据, n <= 20。

对于50%的数据, n <= 1000。

对于100%的数据, n <= 100000。

权值均为不超过1000的正整数。


分析:

        归类为树形dp, 找一个只有三个节点的情况仔细分析一下最容易获得状态转移方程:

  • 叶子节点 vv
    dp[v][0]=0;dp[v][0] = 0;dp[v][1]=weight[i];dp[v][1] = weight[i];

  • 非叶子顶点 vv, 其从做到右第 ii 个孩子结点为 sis_{i}, 其权重为weightvweight_{v}
    dp[v][0]=i=1nmax(dp[si][1],dp[si][0])dp[v][0] = \sum_{i=1}^{n}max(dp[s_{i}][1], dp[s_{i}][0])dp[v][1]=i=1ndp[si][0]+weightvdp[v][1] = \sum_{i = 1}^{n}dp[s_{i}][0] + weight_{v}

  • 最终结果为(rootroot为选择的树的根节点)
    max(dp[root][0],dp[root][1])max(dp[root][0], dp[root][1])


关键点:

  • 怎么存储这个树?

         因为给出两个点但是没有说谁是父亲谁是孩子, 因此使用邻接表存储一个无向图的方式来表示。

  • 超时问题:

         我第一次使用C++提供的STL来常见邻接表, 直接50%的超时。

map<int, vector<int> >;

        后修改为如下结构的方式超时问题解决:

int m = 0;
// 表示一个树结点的链表结点, 使用静态数组的方式加快速度
struct edge
{
    int v, int next;
}edges[200001];
head[100001];      // memset(head, -1, sizeof(head))

void addEdge(int from, int to){
    edge[m].v = to;
    edge[m].next = head[from];
    head[from] = m++;
    
    edge[m].v = from;
    edge[m].next = head[to];
    head[to] = m++;
    
};

  • 如何解决无向图表示的树的后序遍历的无限递归问题

         在DFS函数当中添加一个pre参数, 表明其父亲节点即可, 当孩子不等于父亲则可以DFS。


代码:

#include <bits/stdc++.h>
using namespace std;
int N, t, b, e;
int weights[100001];
int head[100001];           /* 邻接表头*/           
int dp[100001][2];     
int m = 0;                  /* 指针*/
struct edge{                /* 邻接表使用的边节点*/
    int v, next;
}edges[200001];


void addEdge(int from, int to)
{
    // 以to为终点的边, 头查的方式插入
    edges[m].v = to;
    edges[m].next = head[from];
    head[from] = m++;

    // 以from为终点的边
    edges[m].v = from;
    edges[m].next = head[to];
    head[to] = m++;
}

void DFS(int root, int pre)
{
    dp[root][0] = 0;
    dp[root][1] = weights[root];

    // 遍历链表
    // cout << root << endl;
    for(int n = head[root]; n != -1; n = edges[n].next){
        int v = edges[n].v;
    // if(root == 1) cout << v <<endl;
        if(v == pre) continue;
        DFS(v, root);     // 后序遍历
        dp[root][0] += max(dp[v][0], dp[v][1]);
        dp[root][1] += dp[v][0];
    }
    //printf("dp[%d][0]  = %d -- dp[%d][1] = %d\n", root, dp[root][0], root ,dp[root][1]);
}

int main()
{
#ifdef LOCAL
    freopen("ALGO-4.in", "r", stdin);
#endif
    std::ios::sync_with_stdio(false);
    memset(head, -1, sizeof(head));
    int ans = 0;
    cin >> N;
    for(int i = 1; i <= N; ++i)
        cin >> t, weights[i] = t;
    while(cin >> b >> e)
        addEdge(b, e);
    DFS(1, -1); 
    cout << max(dp[1][0], dp[1][1]) << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章