为了更好的阅读体检,可以查看我的算法学习博客
在线评测链接:P1302
题目描述
这天塔子哥在优化他以前做过的一个网络部署的项目,由于软件技术的提升,可以撤销部署网络中的某些节点,以简化网络并降低维护成本。但是,在撤销节点时,不能同时撤销原本直接相邻的两个节点。给定一个满二叉树结构的网络,每个节点上都标有一个数值,表示该节点的年度维护成本。现在要求撤销一些节点,使得可以节省最大的维护成本。
输入的网络以广度优先遍历的方式给出,每个节点都有一个非负整数值表示其年度维护成本。若某个节点不存在,则以 0 0 0表示。每个数值的范围为 0 0 0 到 1000 1000 1000。
输入描述
输入第一行为一个正整数 N N N,表示后面有 N N N个数值。其中 1 ≤ N ≤ 10000 1 \leq N \leq 10000 1≤N≤10000
输入第二行为 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网站注册用户名: 塔子哥学算法) 进行删除。