【备战秋招】每日一题:2023.05.24-华为OD-第三题-网络升级改造

文章介绍了一个关于优化网络部署的问题,目标是撤销某些节点以降低维护成本。给定一个满二叉树结构的网络,每个节点有年度维护成本。解决方案是使用动态规划,定义状态表示子树的最高维护成本,并通过状态转移找到最佳撤销策略。代码示例展示了C++,Python,Java和Go等语言的实现。
摘要由CSDN通过智能技术生成

为了更好的阅读体检,可以查看我的算法学习博客
在线评测链接:P1302

题目描述

这天塔子哥在优化他以前做过的一个网络部署的项目,由于软件技术的提升,可以撤销部署网络中的某些节点,以简化网络并降低维护成本。但是,在撤销节点时,不能同时撤销原本直接相邻的两个节点。给定一个满二叉树结构的网络,每个节点上都标有一个数值,表示该节点的年度维护成本。现在要求撤销一些节点,使得可以节省最大的维护成本。

输入的网络以广度优先遍历的方式给出,每个节点都有一个非负整数值表示其年度维护成本。若某个节点不存在,则以 0 0 0表示。每个数值的范围为 0 0 0 1000 1000 1000

输入描述

输入第一行为一个正整数 N N N,表示后面有 N N N个数值。其中 1 ≤ N ≤ 10000 1 \leq N \leq 10000 1N10000

输入第二行为 N N N个非负整数,表示网络节点每年的维护成本,按照满二叉树的广度优先遍历顺序给出。 0 0 0 表示不存在该关联节点, 0 0 0 只会存在于叶子节点上。

输出描述

输出能够节省的最大维护成本。

样例

输入

7
5 3 5 0 6 0 1

输出

12

解释:

能够节省的最大维护成本为 5 5 5 + 6 + 1 1 1 = 12 12 12

样例2

输入

7
2 7 8 2 4 9 2

输出

19

解释

能够节省的最大维护成本为 2 2 2 + 8 8 8 + 9 9 9 = 19 19 19.

思路

这道题是洛谷P1352-没有上司的舞会一题的改版.具体做法是树形 D P DP DP.

动态规划题目重点有两个:状态定义,状态转移.

状态定义

我们记 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1]为当前以 u u u为根节点的子树的最高维护成本,其中第二维度为0表示 u u u根节点也被撤销,为1表示 u u u根节点未被撤销.

状态转移

我们记 a r r [ u ] arr[u] arr[u] u u u的权值,假设 l e f t left left u u u的左孩子, r i g h t right right u u u的右孩子,那么当我们选择不撤销 u u u时, l e f t left left r i g h t right right必须被撤销.因此 d p [ u ] [ 1 ] = d p [ l e f t ] [ 0 ] + d p [ r i g h t ] [ 0 ] + a r r [ u ] dp[u][1] = dp[left][0]+dp[right][0]+arr[u] dp[u][1]=dp[left][0]+dp[right][0]+arr[u].

现在我们考虑如果 u u u被撤销时该如何选择.

我们发现 u u u被撤销后, l e f t left left r i g h t right right都可以被撤销或者不被撤销.因此,按照贪心的思路,我们就判断 l e f t left left r i g h t right right被撤销和不被撤销的两种情况哪一个维护成本高.具体就是求 m a x ( d p [ l e f t ] [ 0 ] , d p [ l e f t ] [ 1 ] ) max(dp[left][0],dp[left][1]) max(dp[left][0],dp[left][1]) m a x ( d p [ r i g h t ] [ 0 ] , d p [ r i g h t ] [ 1 ] ) max(dp[right][0],dp[right][1]) max(dp[right][0],dp[right][1]).因此最后的转移方程为 d p [ u ] [ 1 ] = m a x ( d p [ l e f t ] [ 0 ] , d p [ l e f t ] [ 1 ] ) + m a x ( d p [ r i g h t ] [ 0 ] , d p [ r i g h t ] [ 1 ] ) dp[u][1] = max(dp[left][0], dp[left][1]) + max(dp[right][0], dp[right][1]) dp[u][1]=max(dp[left][0],dp[left][1])+max(dp[right][0],dp[right][1])

类似题目推荐

见知识点专栏-树上dp

代码

CPP

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> arr;
vector<vector<int>> dp;

void dfs(int u) {
    int left = u * 2 + 1;
    int right = u * 2 + 2;
    if (left >= arr.size()) {//如果是叶子节点,求到dp后直接return
        dp[u][0] = 0;
        dp[u][1] = arr[u];
        return;
    }
    dfs(left);//非叶子节点,就要先求到左子树的最大维护成本
    dfs(right);//和右子树的最大维护成本
    dp[u][0] = max(dp[left][0], dp[left][1]) + max(dp[right][0], dp[right][1]);//如果我们不撤销u
    dp[u][1] = dp[left][0] + dp[right][0] + arr[u];//如果我们撤销u
}

int main() {
    int n;
    cin >> n;
    arr.resize(n);
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }

    dp.resize(n, vector<int>(2));//处理输入

    dfs(0);

    cout << max(dp[0][0], dp[0][1]) << endl;

    return 0;
}

python

n = int(input())
arr = [int(i) for i in input().split()]#处理输入
dp = [[0, 0] for i in range(len(arr))]

def dfs(u):
    left = u * 2 + 1
    right = u * 2 + 2
    if left >= n:#如果是叶子节点,求到dp后直接return
        dp[u][0] = 0
        dp[u][1] = arr[u]
        return
    dfs(left)#非叶子节点,就要先求到左子树的最大维护成本
    dfs(right)#和右子树的最大维护成本
    dp[u][0] = max(dp[left][0], dp[left][1]) + max(dp[right][0], dp[right][1])#如果我们不撤销u
    dp[u][1] = dp[left][0] + dp[right][0] + arr[u]#如果我们撤销u

dfs(0)
print(max(dp[0][0], dp[0][1]))

Java

import java.util.Scanner;

public class Main {
    static int n;
    static int[] arr;
    static int[][] dp;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        arr = new int[n];
        dp = new int[n][2];

        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }//处理输入

        dfs(0);

        System.out.println(Math.max(dp[0][0], dp[0][1]));
    }

    static void dfs(int u) {
        int left = u * 2 + 1;
        int right = u * 2 + 2;
        if (left >= n) {//如果是叶子节点,求到dp后直接return
            dp[u][0] = 0;
            dp[u][1] = arr[u];
            return;
        }
        dfs(left);//非叶子节点,就要先求到左子树的最大维护成本
        dfs(right);//和右子树的最大维护成本
        dp[u][0] = Math.max(dp[left][0], dp[left][1]) + Math.max(dp[right][0], dp[right][1]);//如果我们不撤销u
        dp[u][1] = dp[left][0] + dp[right][0] + arr[u];//如果我们撤销u
    }
}

Go

package main

import "fmt"

func dfs(u int, arr []int, dp [][]int) {
	left := u*2 + 1
	right := u*2 + 2
	if left >= len(arr) {//如果是叶子节点,求到dp后直接return
		dp[u][0] = 0
		dp[u][1] = arr[u]
		return
	}
	dfs(left, arr, dp)//非叶子节点,就要先求到左子树的最大维护成本
	dfs(right, arr, dp)//和右子树的最大维护成本
	dp[u][0] = max(dp[left][0], dp[left][1]) + max(dp[right][0], dp[right][1])//如果我们不撤销u
	dp[u][1] = dp[left][0] + dp[right][0] + arr[u]//如果我们撤销u
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func main() {
	var n int
	fmt.Scan(&n)

	arr := make([]int, n)
	for i := 0; i < n; i++ {
		fmt.Scan(&arr[i])
	}//处理输入

	dp := make([][]int, n)
	for i := 0; i < n; i++ {
		dp[i] = make([]int, 2)
	}

	dfs(0, arr, dp)

	fmt.Println(max(dp[0][0], dp[0][1]))
}

Js

function dfs(u, arr, dp) {
    const left = u * 2 + 1;
    const right = u * 2 + 2;
    if (left >= arr.length) {//如果是叶子节点,求到dp后直接return
        dp[u][0] = 0;
        dp[u][1] = arr[u];
        return;
    }
    dfs(left, arr, dp);//非叶子节点,就要先求到左子树的最大维护成本
    dfs(right, arr, dp);//和右子树的最大维护成本
    dp[u][0] = Math.max(dp[left][0], dp[left][1]) + Math.max(dp[right][0], dp[right][1]);//如果我们不撤销u
    dp[u][1] = dp[left][0] + dp[right][0] + arr[u];//如果我们撤销u
}

function main() {
    const readline = require('readline');//处理输入
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    let n;
    let arr;

    rl.on('line', (input) => {
        if (!n) {
            n = parseInt(input);
        } else {
            arr = input.split(' ').map(Number);
            rl.close();
        }
    });

    rl.on('close', () => {
        const dp = new Array(n).fill(null).map(() => new Array(2).fill(0));
        dfs(0, arr, dp);
        console.log(Math.max(dp[0][0], dp[0][1]));
    });
}

main();

题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值