trang chủ / bài tập / mincostpath / lời giải

Đường Đi Chi Phí Nhỏ Nhất

Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Tác giả: huunguyenhuunguyen

Lời giải: Đường Đi Chi Phí Nhỏ Nhất

Hướng tiếp cận

Đặt dp[x][y] là chi phí nhỏ nhất để đến ô (x,y) từ (1,1).

Công thức truy hồi: dp[x][y]=min(dp[x][y1]+x2, dp[x1][y]+cy)

N lên đến 109, không thể lưu toàn bộ mảng dp. Thay vào đó, ta xử lý từng cột y từ trái sang phải và duy trì hàm fy(x)=dp[x][y] dưới dạng hàm bậc hai từng đoạn (piecewise quadratic).

Nhận xét quan trọng

  1. Tính lồi (convexity): Với mỗi cột y cố định, fy(x) là hàm lồi theo x. Cụ thể, fy(x+1)fy(x)fy(x+2)fy(x+1) — hiệu sai phân không giảm.

  2. Biểu diễn từng đoạn: Mỗi đoạn của fy có dạng: fy(x)=x2·(yj)+x·cj+k trong đó j là "cột nguồn" (cột mà ta di chuyển sang phải lần cuối), và k là hằng số tích lũy.

  3. Cập nhật sang cột mới: Chuyển từ fy1 sang fy (thêm cy vào chi phí đi xuống): fy(x)=minix(fy1(i)+i2+(xi)·cy)=cy·x+minix(fy1(i)+i2i·cy) Đây là "prefix minimum" của một hàm lồi — kết quả vẫn là hàm lồi.

Thuật toán

Duy trì stack các đoạn:

Mỗi phần tử stack lưu (lx, pre_col, k):

  • lx: hàng bắt đầu của đoạn này
  • pre_col: cột nguồn j (cột di chuyển phải gần nhất)
  • k: hằng số; fy(x)=x2(yj)+x·cj+k với xlx

Khi chuyển sang cột y mới:

  1. Lần lượt xóa các đoạn ở đuôi stack nếu chúng bị "thống trị" bởi đoạn mới (đoạn từ việc đi xuống ở cột y hiện tại).
  2. Tìm vị trí breakpoint chính xác bằng tìm kiếm nhị phân.
  3. Thêm đoạn mới vào stack (tương ứng với việc di chuyển xuống từ breakpoint trở đi).

Trả lời truy vấn: Với truy vấn (x,y), tìm kiếm nhị phân trên stack của cột y để xác định đoạn chứa hàng x, rồi tính giá trị trực tiếp.

Khởi tạo cột 1: dp[x][1]=(x1)·c1, tức eval(pre_col=0, k=-c_0, x, col=0) = x \cdot c_0 - c_0.

Độ phức tạp

  • Thời gian: O((M+Q)logN) — mỗi đoạn được thêm/xóa khỏi stack tối đa 1 lần, tìm kiếm nhị phân O(logN); mỗi truy vấn O(logM).
  • Bộ nhớ: O(M+Q)

Bình luận

Không có bình luận tại thời điểm này.

gnatmake 12.2.0 a68g 3.1.2 nasm 2.16.1 as_x64 2.46 awk 1.3.4 gcc 16.1.0 csc 6.12.0.200 g++ 16.1.0 g++-themis 16.1.0 g++17 16.1.0 g++20 16.1.0 g++23 16.1.0 clang++ 22.1.6 dmd 2.112.0 dart 3.12.1 gforth 0.7.3 gfortran 12.2.0 go 1.26.3 groovyc 5.0.6 javac 25.0.3 node 26.2.0 kotlinc 2.3.21 sbcl 2.2.9 lua 5.4.8 nim 2.2.10 fpc 3.2.2 fpc-themis 3.2.2 perl 5.36.0 php 8.5.6 pike 8.0 pypy3 7.3.23 python3 3.14.5 racket 8.7 ruby 4.0.5 rustc 1.96.0 csc 5.3.0 ctoj-scratch 0.0.1 sed 4.9 tclsh 8.6 bun 1.3.14 deno 2.8.1 v 0.5.1 zig 0.16.0