首页 > 试题广场 >

打家劫舍(三)

[编程题]打家劫舍(三)
  • 热度指数:1036 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
你是一个经验丰富的小偷,经过上次在街边和湖边得手后你准备挑战一次自己,你发现了一个结构如二叉树的小区,小区内每个房间都存有一定现金,你观察到除了小区入口的房间以外每个房间都有且仅有一个父房间和至多两个子房间。
问,给定一个二叉树结构的小区,如之前两次行动一样,你无法在不触动警报的情况下同时偷窃两个相邻的房间,在不触动警报的情况下最多的偷窃金额。

1.如果输入的二叉树是
5
2 1 2 2 1
0 1 1 2 3
那么形状结构如下:


小区入口的房间的值是2 ,偷窃第一层2和第三层 2,1 是最优方案。


2.如果输入的二叉树是
3
3 2 10
0 1 1
那么形状结构如下:

样例2:小区入口的房间的值是3 ,偷窃第二层 2,10 是最优方案。

数据范围:二叉树节点数量满足 ,树上的值满足

输入描述:
第一行输入一个正整数 n 表示节点的数量。
第二行输入 n 个正整数,其中第 i 个节点表示序号是 i 的节点的值。
第三行输入 n 个整数,其中第 i 个数表示序号是 i 的节点的父节点的序号。(0表示是根节点)



输出描述:
输出最优方案的的偷窃金额。
示例1

输入

5
2 1 2 2 1
0 1 1 2 3

输出

5
示例2

输入

3
3 2 10
0 1 1

输出

12
#include <bits/stdc++.h>
using namespace std;
const int M = 1e5 + 7;
int v[M], f[M], dp[M][2];//两个状态,偷和不偷
vector<int> tr[M];
void dfs(int root){
    dp[root][0] = 0;//不偷当前节点
    dp[root][1] = v[root];//偷当前节点
    for(auto i : tr[root]){
        dfs(i);
        dp[root][0] += max(dp[i][0], dp[i][1]);//不偷当前节点则可由子节点最大状态转移过来
        dp[root][1] += dp[i][0];//偷当前节点则仅由子节点不偷状态转移过来
    }
}
int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> v[i];
    for(int i = 1; i <= n; i++){
        cin >> f[i];
        tr[f[i]].push_back(i);//建树
    }
    int root = tr[0][0];
    dfs(root);
    cout << max(dp[root][0], dp[root][1]) << endl;
    return 0;
}


发表于 2022-04-14 11:56:30 回复(0)
写一个非常好理解的代码,dfs+dp

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int dp[100002]; // dp[i]为以i为根节点最大可偷的钱数

int dfs(int root, vector<vector<int>> &parents, vector<int> &price){
    if(root==0||parents[root].empty()){
        dp[root] = price[root]; //price[0]=0, 为空节点的价值
        return dp[root];
    }
    if(dp[root]!=-1) return dp[root];
    int l=0, r=0, ll=0, lr=0, rr=0, rl=0; //子树的根,初始为空节点
    l = parents[root][0];
    if(parents[l].size()==1){ //左子树是否有左子树
        ll = parents[l][0];
    }else if(parents[l].size()==2){ //左子树是否有左右子树
        ll = parents[l][0];
        lr = parents[l][1];
    }
    if(parents[root].size()==2){ //判断是否有右子树
        r = parents[root][1];
        if(parents[r].size()==1){ //右子树是否有左子树
            rl = parents[r][0];
        }else if(parents[r].size()==2){ //右子树是否有左右子树
            rl = parents[r][0];
            rr = parents[r][1];
        }
    }
    int res = price[root] + dfs(ll,parents,price) + dfs(lr,parents,price) + dfs(rl,parents,price) + dfs(rr,parents,price);
    res = max(res, dfs(l, parents, price)+dfs(r, parents, price));
    dp[root] = res;
    return dp[root];
}

int main() {
    int n; cin>>n;
    vector<int> price(n+1,0);
    vector<vector<int>> parents(n+1);
    for(int i=1; i<=n; i++) cin>>price[i];
    for(int i=1; i<=n; i++){
        int parent; cin>>parent;
        parents[parent].push_back(i);
    }
    memset(dp, -1, sizeof(dp));
    cout<<dfs(1, parents, price);
}


编辑于 2024-01-04 17:08:34 回复(0)
贴一个Java 的
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] values = new int[n+1];
        for(int i =1;i<=n;i++){
            values[i] = in.nextInt();
        }
        //构建树
        int[] tree = new int[n+1];
        for(int i =1;i<=n;i++){
            tree[i] = in.nextInt();
        }

        //f代表偷,g代表不偷当前层
        int[] f = new int[n+1];
        int[] g = new int[n+1];

        //因为f表示该节点必偷,所以可以初始化为该节点的价值
        for(int i = 1;i<=n;i++){
            f[i]=values[i];
        }
        
        //自底向上
        for(int i =n;i>1;i--){
            f[tree[i]] += g[i]; //因为f初始化为该节点的价值,所以不用再加values[tree[i]] 这样也避免了重复的加入
            g[tree[i]] += Math.max(f[i],g[i]);
        }

        int res = Math.max(f[1],g[1]);
        System.out.println(res);

        
    }
}


发表于 2023-11-26 13:33:28 回复(0)
#include <algorithm>
#include <bits/stdc++.h>
#include <climits>
using namespace std;

int n;
vector<int> value;
vector<int> parent;
vector<int> lchild;
vector<int> rchild;
int ans = INT_MIN;

//返回值为2个,第一个为偷root的值,第二个为不偷root的值
pair<int, int> dp(int root){
    // 空节点返回 0
    if(root == -1)
        return {0, 0};
   
    int lroot = lchild[root];
    int rroot = rchild[root];
    int cval = value[root];

    auto lval = dp(lroot);
    auto rval = dp(rroot);

    //如果偷root,则子节点只能不偷
    auto hasRoot = cval + lval.second + rval.second;
    //如果不偷root,则子节点都选择最大的
    auto noRoot = max(lval.first, lval.second) + max(rval.first, rval.second);

    return {hasRoot, noRoot};
}

int main() {
    cin >> n;
    value.resize(n + 1, 0);
    parent.resize(n + 1, -1);
    lchild.resize(n + 1, -1);
    rchild.resize(n + 1, -1);
    int root = -1;
    for(int i = 1; i <= n; i ++){
        int c;
        cin >> c;
        value[i] = c;
    }

    for(int i = 1; i <= n; i ++){
        int p;
        cin >> p;
       
        if(p == 0) root = i;

        parent[i] = p;
        if(lchild[p] == -1){
            lchild[p] = i;
        }else{
            rchild[p] = i;
        }
       
    }
    /*
    for(auto p : parent)
        cout << p << " ";
    cout << endl;
    for(auto l : lchild)
        cout << l << " ";
    cout << endl;
    for(auto r : rchild)
        cout << r << " ";
    cout << endl;*/
    auto res = dp(root);
    cout << max(res.first, res.second);

}
// 64 位输出请用 printf("%lld")
发表于 2023-08-13 17:17:56 回复(0)
递归,好理解
#include<iostream>
#include<set>
using namespace std;

set<int> childs[100001];
int val[100001];

int dphave(int n);
int dpnone(int n);

int main(){
  int n;
  cin>>n;
  for(int i=1;i<=n;i++) cin>>val[i];
  for(int i=1;i<=n;i++) {
    int parent;
    cin>>parent;
    childs[parent].insert(i);
  }
  cout<<max(dphave(1),dpnone(1))<<endl;
}

int dpnone(int node){
  static int cache[100009];
  if(cache[node]) return cache[node];
  int result=0;
  for(int child:childs[node]){
    result+=max(dphave(child),dpnone(child));
  }
  return cache[node]=result;
}

int dphave(int node){
  static int cache[100009];
  if(cache[node]) return cache[node];
  int result=val[node];
  for(int child:childs[node]){
    result+=dpnone(child);
  }
  return cache[node]=result;
}


发表于 2022-09-03 21:04:55 回复(0)

问题信息

难度:
5条回答 1458浏览

热门推荐

通过挑战的用户

查看代码