2021-04-04:给定一个非负数组arr,和一个正数m。 返回arr的所有子序列中累加和%m之后的最大值。

福大大架构师每日一题 · · 334 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

2021-04-04:给定一个非负数组arr,和一个正数m。 返回arr的所有子序列中累加和%m之后的最大值。

福大大 答案2021-04-04:

自然智慧即可。
1.递归,累加和。
2.动态规划,累加和。
3.动态规划,累加和%m。
4.双向动态规划,累加和%m。

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

package main

import (
    "fmt"
    "math/rand"
    "sort"
    "time"
)

func main() {
    rand.Seed(time.Now().Unix())
    const TOTAL = 500
    RightCount := 0
    for i := 0; i < TOTAL; i++ {
        arr := NewRandArr()
        m := rand.Intn(200) + 1
        fmt.Println(arr, m)
        ret1 := max1(arr, m)
        fmt.Println("1.递归,累加和:", ret1)
        ret2 := max2(arr, m)
        fmt.Println("2.动态规划,累加和:", ret2)
        ret3 := max3(arr, m)
        fmt.Println("3.动态规划,累加和%m:", ret3)
        ret4 := max4(arr, m)
        fmt.Println("4.双向动态规划,累加和%m:", ret4)
        fmt.Println("---------------------")
        if ret1 == ret2 && ret1 == ret3 && ret1 == ret4 {
            RightCount++
        }
    }
    fmt.Println("总数:", TOTAL)
    fmt.Println("正确:", RightCount)
}

//递归,算出所有子序列的累加和
func max1(arr []int, m int) int {
    set := make(map[int]struct{})
    process(arr, 0, 0, set)
    max := 0
    for sum, _ := range set {
        max = getMax(max, sum%m)
    }
    return max
}

func process(arr []int, index int, sum int, set map[int]struct{}) {
    if index == len(arr) {
        set[sum] = struct{}{}
    } else {
        process(arr, index+1, sum, set)
        process(arr, index+1, sum+arr[index], set)
    }
}
func getMax(a int, b int) int {
    if a > b {
        return a
    } else {
        return b
    }
}

//2.动态规划,算出所有的累加和
func max2(arr []int, m int) int {
    sum := 0
    N := len(arr)
    for i := 0; i < N; i++ {
        sum += arr[i]
    }
    dp := make([][]bool, N)
    for i := 0; i < N; i++ {
        dp[i] = make([]bool, sum+1)
    }
    for i := 0; i < N; i++ {
        dp[i][0] = true
    }
    dp[0][arr[0]] = true
    for i := 1; i < N; i++ {
        for j := 1; j <= sum; j++ {
            dp[i][j] = dp[i-1][j]
            if j-arr[i] >= 0 {
                dp[i][j] = dp[i][j] || dp[i-1][j-arr[i]]
            }
        }
    }
    ans := 0
    for j := 0; j <= sum; j++ {
        if dp[N-1][j] {
            ans = getMax(ans, j%m)
        }
    }
    return ans
}

//3.动态规划,算出所有的模m的累加和。数组长度巨大,m不大。
func max3(arr []int, m int) int {
    N := len(arr)
    // 0...m-1
    dp := make([][]bool, N)
    for i := 0; i < N; i++ {
        dp[i] = make([]bool, m)
    }
    for i := 0; i < N; i++ {
        dp[i][0] = true
    }
    dp[0][arr[0]%m] = true
    for i := 1; i < N; i++ {
        for j := 1; j < m; j++ {
            // dp[i][j] T or F
            dp[i][j] = dp[i-1][j]
            cur := arr[i] % m
            if cur <= j {
                dp[i][j] = dp[i][j] || dp[i-1][j-cur]
            } else {
                dp[i][j] = dp[i][j] || dp[i-1][m+j-cur]
            }
        }
    }
    ans := 0
    for i := 0; i < m; i++ {
        if dp[N-1][i] {
            ans = i
        }
    }
    return ans
}

// 如果arr的累加和很大,m也很大
// 但是arr的长度相对不大
func max4(arr []int, m int) int {
    if len(arr) == 1 {
        return arr[0] % m
    }
    mid := (len(arr) - 1) / 2

    sortSet1 := make(map[int]struct{})
    process4(arr, 0, 0, mid, m, sortSet1)
    sortSet2 := make(map[int]struct{})
    process4(arr, mid+1, 0, len(arr)-1, m, sortSet2)
    ans := 0

    s1 := make([]int, 0)
    for key, _ := range sortSet1 {
        s1 = append(s1, key)
    }
    sort.Ints(s1)
    //fmt.Println("s1:", s1)

    s2 := make([]int, 0)
    for key, _ := range sortSet2 {
        s2 = append(s2, key)
    }
    sort.Ints(s2)
    //fmt.Println("s2:", s2)

    for _, leftMod := range s1 {
        //ans = getMax(ans, leftMod + sortSet2.floor(m - 1 - leftMod));
        index := NearestIndex2(s2, m-1-leftMod)
        if index >= 0 {
            ans = getMax(ans, leftMod+s2[index])
        } else {
            ans = getMax(ans, leftMod)
        }
    }
    return ans
}

// 在arr上,找满足<=value的最右位置
func NearestIndex2(arr []int, v int) int {
    L := 0
    R := len(arr) - 1
    index := -1 // 记录最右的对号
    for L <= R {
        mid := L + (R-L)>>1
        if arr[mid] <= v {
            index = mid
            L = mid + 1
        } else {
            R = mid - 1
        }
    }
    return index
}

// 从index出发,最后有边界是end+1,arr[index...end]
func process4(arr []int, index int, sum int, end int, m int, sortSet map[int]struct{}) {
    if index == end+1 {
        sortSet[sum%m] = struct {
        }{}
    } else {
        process4(arr, index+1, sum, end, m, sortSet)
        process4(arr, index+1, sum+arr[index], end, m, sortSet)
    }
}

func NewRandArr() []int {
    arrLen := rand.Intn(10) + 5
    arr := make([]int, arrLen)
    for i := 0; i < arrLen; i++ {
        arr[i] = rand.Intn(50)
    }
    return arr
}

执行结果如下:


在这里插入图片描述

左神java代码
评论


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

334 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传