Hướng dẫn giải của Tiệc sinh 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 làm lời giải.
Nộp code mẫu trước khi tự giải được bài tập là một hành vi có thể bị ban.
Tác giả:
Nhận xét 1: Để tối ưu thì Jessie luôn di chuyển theo thứ tự từ ngôi nhà có thứ tự nhỏ đến ngôi nhà có thứ tự lớn hơn.
Nhận xét 2: Nếu ta biết trước chuyến đi của Jessie bắt đầu từ ngôi nhà và kết thúc ở ngôi nhà , thì tổng độ yêu thích tối đa, kí hiệu là sẽ được xác định bằng công thức:
Nói cách khác, do khoảng cách di chuyển là cố định, nên độ hài lòng tối đa sẽ phụ thuộc vào độ yêu thích tối đa, và có thể được xác định một cách tham lam bằng cách xác định ngôi nhà có độ yêu thích cao nhất trong đoạn từng món quà.
Như vậy ta có thể dễ dàng giải được bài toán với độ phức tạp với chi phí tìm ~\max C_{ij}~ là . Ta có thể dùng bảng thưa để tối ưu chi phí thành và độ phức tạp thành , tương ứng với 4 subtask đầu tiên của đề bài.
Gọi là độ hài lòng tối ưu nếu Jessie kết thúc hành trình tại ngôi nhà thứ , ta có:
với là tổng chi phí di chuyển từ nhà đến nhà .
Gọi là vị trí tối ưu cho . Ta nhận thấy rằng
Vì nếu thì luôn tồn tại cách tối ưu hơn bằng cách Jessie xuất phát từ .
Từ nhận xét này, ta có thể áp dụng kĩ thuật chia để trị để tối ưu hóa chi phí chuyển quy hoạch động từ thành . Và độ phức tạp của thuật toán là
int n, m; const int N = 5005, M = 205, LOG = 13; ll d[N]; ll c[N][M]; ll dp[N]; ll table[M][N][LOG]; ll prefix[N]; const ll INF = 1e18; void Input() { cin >> n >> m; reu(i, 1, n-1) cin >> d[i]; reu(i, 1, n) reu(j, 1, m) cin >> c[i][j]; } ll query(int x, int L, int R) { int k = __lg(R-L+1); return max(table[x][L][k], table[x][R - MASK(k) + 1][k]); } ll calc(int L, int R) { ll ans = 0; reu(x, 1, m) ans += query(x, L, R); return ans - prefix[R-1] + (L ? prefix[L-1] : 0); } void DP(int L, int R, int optL, int optR) { if (L > R) return; int mid = (L+R) >> 1; ll best = -INF; int optM = -1; reu(i, optL, min(optR, mid)) { if (maximize(best, calc(i, mid))) optM = i; } dp[mid] = best; DP(L, mid-1, optL, optM); DP(mid+1, R, optM, optR); } void Solve() { reu(i, 1, n) prefix[i] = prefix[i-1] + d[i]; //build table reu(i, 1, n) reu(x, 1, m) table[x][i][0] = c[i][x]; reu(x, 1, m) reu(k, 1, LOG-1) for(int i = 1; i + MASK(k) - 1 <= n; ++i) { table[x][i][k] = max(table[x][i][k-1], table[x][i + MASK(k-1)][k-1]); } reu(i, 1, n) dp[i] = c[i][1]; DP(1, n, 1, n); ll ans = 0; reu(i, 1, n) maximize(ans, dp[i]); cout << ans; }
Bình luận