HDU1520

该题目的意思是,给定一棵树,每个点有个权值,目标是选择某些点使总权值和最大,但是要求任意一对父节点和子节点不能一起选择。

思路:DP。说的高大尚一点就是树形DP。

从子节点往根节点走,DP[ i ][ 0 ]表示以i为顶点的子树,不选 i 点的情况下最大值。
DP[ i ][ 1 ]表示以 i 为顶点的子树,选 i 点的情况下最大值。

由此就可以想到如下转移方程:

dp[  i ][ 0 ] = sum{  max  {   dp[ j ][ 0 ], f[ j ][ 1 ]  }, j 是 i 的儿子节点}

该条的意思就是,当不选i点时,它的儿子节点可以不选也可以选,所以对每个子节点选或不选的情况取个最大的和。
dp[ i ][ 1 ] = val[ i ] + sum{ dp[ j ][ 0 ], j 是 i 的儿子节点}

这条的意思是当选了 i 点时,它的儿子节点必须是不能选,所以直接取所有子节点不选的和。

DFS的作用是实现从底向上,虽然表面写法是从顶向下,但是回溯回来之后便是从底向上。

tree数组的作用是找寻根节点(其实也是由输入来决定的根节点),或者说是选择一个点作为根节点更准确。

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<vector>  
#include<queue>  
#include<cmath>  
#include<cstring>  
using namespace std;  
#define INF 0x3f3f3f3f;  
vector<int>q[10000];  
int tree[10000];  
int val[10000];  
int dp[10000][2];  
int vis[10000];  
int n,m;  
  
void dfs(int u){  
    vis[u] = true;  
    dp[u][0] = 0;  
    dp[u][1] = val[u];  
    for(int i=0; i<q[u].size(); ++i){  
        int v = q[u][i];  
        if(vis[v]) continue;  
        dfs(v);  
        dp[u][0] += max(dp[v][1], dp[v][0]);  
        dp[u][1] += dp[v][0];  
    }  
}  
  
int main(){  
  
    while(~scanf("%d", &n) && n)
    {  
        for(int i=1; i<=n; ++i) 
            q[i].clear();    
        for(int i=1; i<=n; ++i)  
            scanf("%d", &val[i]);   
        memset(tree, 0, sizeof(tree));    
            int u, v;  
        while(~scanf("%d%d", &v, &u) && v+u)
        {  
            q[u].push_back(v);  
            ++tree[v];  
        }    
        memset(dp, 0, sizeof(dp));  
        for(int i=1; i<=n; ++i)if(!tree[i])
        {  
            memset(vis, 0, sizeof(vis));  
            dfs(i);  
            printf("%d\n", max(dp[i][0], dp[i][1]));  
            break;  
        }  
    }  
  
    return 0;  
}  

猜你喜欢

转载自www.cnblogs.com/fantastic123/p/8965682.html