hdu 1520 Anniversary party(树形dp)

本文介绍了一种解决树形结构中寻找最大权值子集的方法,通过深度优先搜索和动态规划实现。重点阐述了如何利用树的特性进行状态转移,最终求得整棵树的最大权值解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


//树形dp入门第一题

// 题意: 给一棵树每个节点都有权值 子节点和父节点不能同时取 问能取得权值之和最大是多少 看似有向边其实为无向边
//
//从任意一个节点x dfs到叶节点 并计算叶节点的值 然后边回溯边dp 求当前节点的最优状态 当回溯到x时整棵树的最优解就得到了
//dp[x][0]表示不取节点x得到的最优解 dp[x][1]表示取节点x 当取该节点时 就不能取它的父节点(y)和子节点(z)了 所以 dp[x][1]=dp[z][0]+dp[y][0]+num[x];
//不取当前节点 则可以取父节点和子结点 也可以不取 取两者的最大值结可以了  dp[x][0]+=max(dp[k][0],dp[k][1])(k为x的父节点或子结点)
//
//    树      5
//          /  \
//         3    4
//        / \  / \
//       1  2 6   7 
//
//的邻接表建成是这个样子的(建双向边)
//head[1]->3->^
//head[2]->3->^
//head[3]->1->2->5->^
//head[4]->6->7->5->^
//head[5]->3->4->^
//head[6]->4->^
//head[7]->4->^
//
//
#include<stdio.h>
#include<string.h>


#define N 6100


struct node
{
    int v;
    struct node * next;
}*head[N],tree[N*2];//邻接表的节点 v 存树的节点的编号 


int dp[N][2];
int num[N];
int vis[N];


int n,p;


int max(int a,int b){return a>b?a:b;}


void add(int a,int b)//加边 建邻接表 单链表头插法
{
    tree[p].v=b;
    tree[p].next=head[a];head[a]=&tree[p++];
    tree[p].v=a;
    tree[p].next=head[b];head[b]=&tree[p++];
}


void init()
{
    p=1;
    memset(vis,0,sizeof(vis));
    memset(head,NULL,sizeof(head));
    memset(dp,0,sizeof(dp));


    int i;
    for(i=1;i<=n;i++)
        scanf("%d",&num[i]);


    int a,b;


    while(scanf("%d%d",&a,&b),(a+b))
    {
        add(b,a);
    }
    return ;
}


void tree_dp(int x)
{
    if(vis[x])return ;
    vis[x]=1;
    int temp,i;


    struct node * pp;
    pp=head[x];
    while(pp)
    {
        if(!vis[pp->v])//vis[pp->v]=1时 说明当前的pp求pp->v dfs过来 所以不能再dfs回去 需要回溯回去
        {
        tree_dp(pp->v);
        temp=max(dp[pp->v][0],dp[pp->v][1]);
        dp[x][0]+=temp;
        dp[x][1]+=dp[pp->v][0];
        }
        pp=pp->next;
    }
    dp[x][1]+=num[x];


}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        tree_dp(1);
        printf("%d\n",max(dp[1][0],dp[1][1]));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值