Problem Statement

Tóm tắt đề bài:

Cho một ma trận kích thước $N$ dòng, $M$ cột. Mỗi ô trong ma trận có giá trị $a_{i, j}$. Từ một ô $(i, j)$, bạn có thể di chuyển đến ô $(i + 1, j)$ hoặc $(i, j + 1)$. Tuy nhiên không thể đi xuống dưới $3$ lần liên tiếp.

Hãy tìm đường đi có tổng lớn nhất từ ô $(1, 1)$ đến ô $(N, M)$.

Constraints

  • $1 \leq N, M \leq 2025$
  • $-10^9 \leq a_{i, j} \leq 10^5$

Solution

  • Đặt $dp_{i, j, k}$ là giá trị lớn nhất có thể đạt được khi đến ô $(i, j)$ với $k$ lần đi xuống liên tiếp.

  • Ta sẽ có một số công thức chuyển trạng thái như sau:
    • Nếu $k = 0$, ta có: $dp_{i, j, 0} = max(dp_{i, j - 1, p_{0 \leq p \leq 2}}) + a_{i, j}$.
    • Nếu $k \geq 1$, ta có: $dp_{i, j, k} = max(dp_{i - 1, j, k - 1}) + a_{i, j}$.
  • Trường hợp cơ sở: $dp_{1, 1, 0} = a_{1, 1}$.
  • Đáp án sẽ là $max(dp_{N, M, k_{0 \leq k \leq 2}})$.

  • Độ phức tạp: $O(N \cdot M)$.

Example Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

const bufferSize = 1000000

var scanner *Scanner

type Scanner struct {
	*bufio.Scanner
}

func (s *Scanner) nextInt() int {
	s.Scan()
	t := s.Text()
	i, _ := strconv.Atoi(t)
	return i
}

func (s *Scanner) nextInt64() int64 {
	s.Scan()
	t := s.Text()
	i, _ := strconv.ParseInt(t, 10, 64)
	return i
}

func (s *Scanner) nextWord() string {
	s.Scan()
	return s.Text()
}

var writer *bufio.Writer

func init() {
	scanner = &Scanner{bufio.NewScanner(os.Stdin)}
	buffer := make([]byte, 0, bufferSize)
	scanner.Buffer(buffer, bufferSize)
	scanner.Split(bufio.ScanWords)

	writer = bufio.NewWriter(os.Stdout)
}

func main() {

	n, m := scanner.nextInt(), scanner.nextInt()

	dp := make([][][]int64, n+1)
	for i := 0; i <= n; i++ {
		dp[i] = make([][]int64, m+1)
		for j := 0; j <= m; j++ {
			dp[i][j] = make([]int64, 3)
			for k := 0; k <= 2; k++ {
				dp[i][j][k] = int64(-1e15)
			}
		}
	}

	for i := 1; i <= n; i++ {
		for j := 1; j <= m; j++ {
			x := scanner.nextInt()
			for k := 0; k <= 2; k++ {
				if i == 1 && j == 1 && k == 0 {
					dp[i][j][k] = int64(x)
				} else if j > 1 && k == 0 {
					for p := 0; p <= 2; p++ {
						dp[i][j][k] = max(dp[i][j][k], dp[i][j-1][p]+int64(x))
					}
				} else if i > 1 && k >= 1 {
					dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k-1]+int64(x))
				}
			}
		}
	}

	res := max(dp[n][m][0], dp[n][m][1], dp[n][m][2])

	fmt.Fprint(writer, res)

	writer.Flush()
}