有一家咖啡馆实行以下折扣制度:每购买more美元以上more美元,买家就会收到一张优惠券,有权获得一顿免费午餐. 您有future N天的价目表,如下所示:

5
35
40
101
59
63

你决定每天吃饭,所以找出午餐可能的最低总成本,以及你应该使用优惠券的天数. 请帮助稳定DP履行.我不确定初始DP表是否适合该任务,因此我无法正确处理边缘情况:

func main() {
    file, _ := os.ReadFile("input.txt")

    lines := strings.Split(string(file), "\n")

    p := make([]int, 0, len(lines))

    for i := 0; i < len(lines); i++ {
        price, err := strconv.Atoi(lines[i])

        if err != nil {
            continue
        }

        p = append(p, price)
    }

    dp := make([][]int, len(p)+1)
    dp[0] = make([]int, len(dp))
    
    // zero day row
    for i := 1; i < len(dp); i++ {
        // no coupons on the first day visit
        dp[0][i] = math.MaxInt32
    }
    
    // 1...N days
    for i := 1; i < len(dp); i++ {
        dp[i] = make([]int, len(p))
    }

    for i := 1; i <= len(p); i++ {
        for j := 0; j < len(p); j++ {
            if p[i-1] <= 100 {
                dp[i][j] = Min(dp[i-1][j]+p[i-1], dp[i-1][j+1])
            }

            dp[i][j] = Min(dp[i-1][j-1]+p[i-1], dp[i-1][j+1])
        }
    }

    fmt.Println(dp)
}

func Min(i, j int) int {
    if i <= j {
        return i
    }

    return j
}

谢谢.

UPD.看似达到最佳效果但未被3RT方测试系统接受的扩展解决方案:

package main

import (
    "fmt"
    "math"
    "os"
    "sort"
    "strconv"
    "strings"
)

func main() {
    file, _ := os.ReadFile("input.txt")

    lines := strings.Split(string(file), "\n")

    p := make([]int, 0, len(lines))

    for i := 1; i < len(lines); i++ {
        price, err := strconv.Atoi(lines[i])

        if err != nil {
            continue
        }

        p = append(p, price)
    }

    l := len(p)

    dp := make([][]int, l+1)
    dp[0] = make([]int, len(dp))

    for i := 1; i < len(dp); i++ {
        dp[0][i] = math.MaxInt32
    }

    for i := 1; i < len(dp); i++ {
        dp[i] = make([]int, l+1)
    }

    // fill dp
    for i := 1; i <= l; i++ {
        for j := 0; j <= l; j++ {
            if p[i-1] <= 100 {
                if j == l {
                    dp[i][j] = MinOrZero(dp[i-1][j]+p[i-1], dp[i-1][j])
                    continue
                }

                dp[i][j] = MinOrZero(dp[i-1][j]+p[i-1], dp[i-1][j+1])
                continue
            }

            if j == 0 {
                dp[i][j] = MinOrZero(math.MaxInt32, dp[i-1][j+1])
                continue
            }

            if j == l {
                dp[i][j] = MinOrZero(dp[i-1][j-1]+p[i-1], dp[i-1][j])
                continue
            }

            dp[i][j] = MinOrZero(dp[i-1][j-1]+p[i-1], dp[i-1][j+1])
        }
    }
    
    // coupons left
    coupons := 0
    min := math.MaxInt

    for j := l; j >= 0; j-- {
        sum := dp[l][j]

        if sum < min {
            min = sum
            coupons = j
        }
    }

    couponDays := make([]int, 0)
    j := coupons
    sum := min

    for i := l; i > 0; i-- {
        jl := j - 1
        js := j
        jr := j + 1

        if jr < l && dp[i-1][jr] == sum {
            couponDays = append(couponDays, i)
            j++
            continue
        }

        if dp[i-1][js] == sum {
            sum = dp[i-1][js]
            continue
        }

        if jl >= 0 && dp[i-1][jl] == sum-p[i-1] {
            sum = dp[i-1][jl]
            j--
            continue
        }

        sum -= p[i-1]
    }

    sort.Ints(couponDays)

    fmt.Println(min)
    fmt.Println(fmt.Sprintf("%d %d", coupons, len(couponDays)))
    for _, day := range couponDays {
        fmt.Println(day)
    }
}

func Min(i, j int) int {
    if i <= j {
        return i
    }

    return j
}

func MinOrZero(i, j int) int {
    var result int

    if i <= j {
        result = i
    } else {
        result = j
    }

    if result >= 0 {
        return result
    }

    return 0
}

推荐答案

为了能够退还优惠券的使用日期,您需要跟踪最佳路径,然后您可以在结束时倒退.

当使用或获得票证时,您必须非常小心,因为它会移动DP数组中的位置.一些职位可以保持不确定.例如,如果你在第一天得到一张票,那么对应于0张票的单元格将保持未定义,因为当天的成本被设置为1张票(如果第二天的价格高于100,我们最终得到的是0或2张票,而不是1).如果这种上下 skip 做得正确,它应该会起作用.

function solve(prices) {
  const dp = [[]], n = prices.length
  let tickets = prices[0] > 100 ? 1 : 0
  dp[0][tickets] = {cost: prices[0]}
  for (let i = 1; i < n; i++) {
    dp.push([])
    let t = prices[i] > 100 ? 1 : 0
    // not using tickets
    for (let j = 0; j <= tickets; j++)
      if (dp[i - 1][j])
        dp[i][j + t] = {cost: dp[i - 1][j].cost + prices[i], prev: j}
    // using tickets
    for (let j = 1; j <= tickets; j++)
      if (dp[i - 1][j] && (!dp[i][j - 1] || dp[i][j - 1].cost > dp[i - 1][j].cost))
        dp[i][j - 1] = {cost: dp[i - 1][j].cost, prev: j}
    tickets += t
  }
  // min has to be at 0 or 1 ticket
  let idx = dp[n - 1][0] && (!dp[n - 1][1] || dp[n - 1][0].cost <= dp[n - 1][1].cost) ? 0 : 1
  console.log('costs: ' + dp[n - 1][idx].cost)
  const days = []
  for (let day = n - 1; day > 0; day--) {
    if (dp[day][idx].prev > idx)
      days.unshift(day)
    idx = dp[day][idx].prev
  }
  console.log('days: ' + days)
}

solve([88,55,101,77,44,130,160,22,55,97,101,2,88])

Go相关问答推荐

在Go中根据命名空间/字符串生成UUID

如何在jsonrpc服务器的服务器端捕获错误?

GO:如何指定类型约束,使S方法的参数类型与接收方的参数类型相同

理解Golang中的IOTA和常量

迭代字符串并用映射值替换原始字符串中的值的惯用方法

为什么 Go 对于长度为 100k 的切片使用的内存比长度为 100k 的数组要少?

使用Dockertest进行Golang SQL单元测试的基本设置

判断一个区域内的纬度/经度点

在 .go 文件中运行一个函数和在 Go 模板中调用它有什么区别?

regex.ReplaceAll 但如果替换则添加相同数量的字符

如何确定作为函数参数传递的指针是否正在被修改或副本是否正在被修改?

如何使用带有方法的字符串枚举作为通用参数?

Go 中的 HTTP 请求验证中间件

如何将多个切片打印为一个切片?

使用 package`regexp` 查找 Golang 中的所有 mactch 子字符串,但得到意外结果

如何在Go中替换符号并使下一个字母大写

如何从应用程序调用谷歌云身份 API

如何动态解析 Go Fiber 中的请求正文?

将函数的值作为输入参数返回给另一个

Golang 中的无实体函数