2022-04-14:小美有一个长度为n的数组, 为了使得这个数组的和尽量大,她向会魔法的小团进行求助。 小团可以选择数组中至多两个不相交的子数组, 并将区间里的数全都变为原来的10倍。 小团想知道他的魔法最多可以帮助小美将数组的和变大到多少? 来自美团。

答案2022-04-14:

动态规划。 时间复杂度:O(N)。 空间复杂度:O(N)。

代码用rust编写。代码如下:

#![feature(core_intrinsics)]
fn print_type_of<T>(_: T) {
    println!("{}", unsafe { std::intrinsics::type_name::<T>() });
}

fn main(){
    let arr: Vec<i32> = vec![3, -4, 5, 1, -3];
    let ret:i32 = max_sum2(arr);
    println!("{}",ret);
    print_type_of(ret);
}


fn max_sum2(arr: Vec<i32>) ->i32 {
	let n = arr.len();
	if n == 0 {
		return 0;
	}
	if n == 1 {
		return get_max(arr[0], arr[0]*10);
	}
	// dp[i]
	// 1) arr[0...i]原始累加和
	// 2) dp[i-1] + arr[i]
	// 3) magic[i]

	// : arr[0..i]范围上,可以没有10倍区域、或者有10倍区域但是最多有一个的情况下,
	//    最大累加和是多少?
	// 可能性1:就是没有10倍区域,那就是arr[0..i]的累加和, 这个好弄!
	//
	// 可能性2:有一个10倍区域
	//         a : arr[i]不在10倍区域里,但是之前可能有,那么就是dp[i-1] + arr[i]
	//
	//         b : arr[i]在10倍区域里
	//             甲:arr[0..i-1]没有10倍区域,arr[i]自己10倍,arr[0..i-1] + 10 * arr[i]
	//             乙:arr[0..i-1]中i-1位置在10倍区域里,arr[i]也在10倍区域里
	// magic[i] : magic[i] ..i  i
	// 对于乙,要求知道magic[j]的信息
	// magic[j]:arr[0..j]范围上,j一定要在10倍区域里,并且只有一个10倍区域的情况下,最大累加和
	// 可能性1:只有arr[j]是10倍,arr[0..j-1]没有10倍
	// 可能性2:magic[j-1] + 10 * arr[j]
	let mut sum :i32 = arr[(n-1) as usize];
	let mut magic:i32 = sum * 10;
    let mut right:Vec<i32> = Vec::new(); 
    for i in 0..n { 
        right.push(0); 
    } 
    
	right[n-1] = get_max(sum, sum*10);

    let mut i:isize=n as isize -2 as isize;
	while i >= 0 {
		magic = 10*arr[i as usize] + get_max(sum, magic);
		sum = sum + arr[i as usize];
		right[i as usize] = get_max(get_max(sum, right[i as usize + 1] + arr[i as usize]), magic);
        i = i - 1;
	}
	let mut ans:i32 = right[0];
	sum = arr[0];
	magic = sum * 10;
	let mut dp:i32 = get_max(sum, sum * 10);
	ans = get_max(ans, dp + right[1]);
    i=1;
	while i < n as isize-1{
		magic = 10 * arr[i as usize] + get_max(sum, magic);
		sum = sum + arr[i as usize];
		dp = get_max(get_max(sum, dp+arr[i as usize]), magic);
		ans = get_max(ans, dp + right[i as usize + 1]);
        i = i + 1;
	}
	return ans;
}

fn get_max(a:i32, b :i32) ->i32 {
	if a > b {a} else {b}
}

执行结果如下: 在这里插入图片描述

代码用golang编写。代码如下:

package main

import "fmt"

func main() {
	arr := []int{3, -4, 5, 1, -3}
	ret := maxSum2(arr)
	fmt.Println(ret)
}

func maxSum2(arr []int) int {
	n := len(arr)
	if n == 0 {
		return 0
	}
	if n == 1 {
		return getMax(arr[0], arr[0]*10)
	}
	// dp[i]
	// 1) arr[0...i]原始累加和
	// 2) dp[i-1] + arr[i]
	// 3) magic[i]

	// : arr[0..i]范围上,可以没有10倍区域、或者有10倍区域但是最多有一个的情况下,
	//    最大累加和是多少?
	// 可能性1:就是没有10倍区域,那就是arr[0..i]的累加和, 这个好弄!
	//
	// 可能性2:有一个10倍区域
	//         a : arr[i]不在10倍区域里,但是之前可能有,那么就是dp[i-1] + arr[i]
	//
	//         b : arr[i]在10倍区域里
	//             甲:arr[0..i-1]没有10倍区域,arr[i]自己10倍,arr[0..i-1] + 10 * arr[i]
	//             乙:arr[0..i-1]中i-1位置在10倍区域里,arr[i]也在10倍区域里
	// magic[i] : magic[i] ..i  i
	// 对于乙,要求知道magic[j]的信息
	// magic[j]:arr[0..j]范围上,j一定要在10倍区域里,并且只有一个10倍区域的情况下,最大累加和
	// 可能性1:只有arr[j]是10倍,arr[0..j-1]没有10倍
	// 可能性2:magic[j-1] + 10 * arr[j]
	sum := arr[n-1]
	magic := sum * 10
	right := make([]int, n)
	right[n-1] = getMax(sum, sum*10)
	for i := n - 2; i >= 0; i-- {
		magic = 10*arr[i] + getMax(sum, magic)
		sum += arr[i]
		right[i] = getMax(getMax(sum, right[i+1]+arr[i]), magic)
	}
	ans := right[0]
	sum = arr[0]
	magic = sum * 10
	dp := getMax(sum, sum*10)
	ans = getMax(ans, dp+right[1])
	for i := 1; i < n-1; i++ {
		magic = 10*arr[i] + getMax(sum, magic)
		sum += arr[i]
		dp = getMax(getMax(sum, dp+arr[i]), magic)
		ans = getMax(ans, dp+right[i+1])
	}
	return ans
}

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

执行结果如下:

在这里插入图片描述


左神java代码