算法训练 结点选择

问题描述
有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
心路历程
本以为这道题必须要用到树,又看了看别人的博客,发现又是用到无向图,巴拉巴拉,看了好久,没看懂(我只是个平平无奇的小垃圾)
终于,我找到了一篇代码很短的博客,开始仔细研究,又终于,看懂了,ac了,在此感谢原文链接
思路
大佬的博客里,我自己觉得不是很详细,所以我从一个小垃圾的角度谈一谈我的看法,不得不说,这解法,妙啊,省了多少繁琐的建图建树什么的,简单明了,爱
来,action~
首先这道题,我最开始的想法是从树、图上手,嗯,比较麻烦,但如果说这道题用数组来存储,是不是一下子就觉得他简单 了,是的,没错
用一个二维数组:第一维来存储节点,第二维存储该节点所连接的其他节点
那么一说到数组,除了简单的数组,我们一定要想想vector可不可以用,这道题,用vector,方便至极!!!
(当然,普通数组也可)
第二点就是,这道题是选出最合理的方案,一这么说,我们就要想动态规划,没错,动态地决定某个节点是否要选,那么就要想清楚动态转移方程,(emmm,其实我觉得dp[i][j]这个东西里,i、j分别代表什么意思是最最重要的,这真的好难,多做题,maybe没别的办法)
而对于这道题dp数组的含义就是:该节点要不要选(这不是刚好是我们的主题嘛,嗯,以后可以都从主题上入手去想)dp[i][1]、dp[i][0]就分别为i节点选or不选
下一步呢,我们要知道他这个状态转移方程怎么写,这个关键在于我们要知道动态规划的一些小九九,(我自己这么想的,不一定十分准确)first:一定有一个初始的状态给了结果(像这道题就是每一个节点选或不选时的dp值,当然这里说的是只看着某一个节点,不关注其他点)second:一定是要从最后往前推导,我们学算法的时候就知道了:贪心是从前往后,动态规划是从后往前。(这样的话,我们有单个点选或不选的值,然后依次往前推,不就有结果了嘛,嗯哼,棒)
好的,跑题了,说状态转移方程:这个方程都是根据题目给的约束写的,那这道题的约束就是选了某点,就不能选和他挨着的点,对于每一个点:都有选或不选,他选了,那他的上一个点就必须是不选,他不选,他的上一个点可选可不选(看怎样合适,这里我觉得容易坑人嗷)
最后一个点点就是:dfs
毕竟这不是线性结构,我们想要遍历,当然是dfs,这样的话,每一个节点都有他的上一个点,和刚刚说的方程刚好对上,完美!
话这么多,不够严谨,只是思路的一个方向,下面看代码吧(我写了注释,看代码比看文字可能更好理解,还配着注释,哈哈)

#include <bits/stdc++.h>
using namespace std;
vector<int> Graph[100001];
int dp[100001][2];
/*
上面这两个数组的大小一定要注意,我开始直接定义成了100000,因为我看题目给的是小于等于,我觉得够用,但是我忽略了我是从下标为1开始用的,不够了,所以还是大点嗷
*/
void dfs(int f,int r)
{
    for(int i=0;i<Graph[f].size();i++)
    {
        int v=Graph[f][i];
        if(v==r)//这个if判断是为了避免无限循环,因为像1、2这条边,在Graph[1]有,Graph[2]也有,避免又走回去
            continue;
        dfs(v,f);
        dp[f][1]+=dp[v][0];//选这个点,那么必然是上一个点没选的情况(不考虑再再上面嗷,动态规划这个东西,就是把前面看成一个整体,一层一层往回看,当前不用管,后面会知道的)
        dp[f][0]+=max(dp[v][1],dp[v][0]);//不选这个点,这时就要看上一点是选了大还是不选大(毕竟我们还没有考虑上上个点嘛,就是这个意思)
    }
}
int main()
{
    memset(dp,0,sizeof(dp));//数组一定要初始化,很容易坑人的
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>dp[i][1];//选当前节点得到的权值
    }
    int p,q;
    for(int i=1;i<n;i++)
    {
        cin>>p>>q;
        Graph[p].push_back(q);
        Graph[q].push_back(p);
    }
    dfs(1,0);
    cout<<max(dp[1][0],dp[1][1])<<endl;//这里的意思就是呢:1这个点要不要选,因为我们是从下往上看的,看到1时,已经到了最优情况
    return 0;
}

好了,这道题就这样了,表述可能不太清晰,明天再会~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值