1. 程式人生 > 其它 >【洛谷 P3842 [TJOI2007]線段】解題報告(動態規劃)

【洛谷 P3842 [TJOI2007]線段】解題報告(動態規劃)

P3842 解題報告。

題面

在一個 \(n\times n\) 的平面上,在每一行中有一條線段,第 \(i\) 行的線段的左端點是 \((i, L_i)\),右端點是 \((i, R_i)\)

你從 \((1,1)\) 點出發,要求沿途走過所有的線段,最終到達 \((n,n)\) 點,且所走的路程長度要儘量短。

更具體一些說,你在任何時候只能選擇向下走一步(行數增加 \(1\))、向左走一步(列數減少 \(1\))或是向右走一步(列數增加 \(1\))。當然,由於你不能向上行走,因此在從任何一行向下走到另一行的時候,你必須保證已經走完本行的那條線段。

\(1\le n\le 2\times 10^4\)\(1\le L_i\le R_i\le n\)

題解

本題的狀態比較顯然。

由於每一層必然走過整條線段,所以顯然從線段的左/右端點向下走是最優的。我們拆分子問題,求從 \((1,1)\) 走到第 \(i\) 層線段左/右端點的最少步數,可以通過從 \((1,1)\) 走到第 \(i-1\) 層線段左/右端點的最少步數求出,因此從 \((1,1)\) 走到第 \(i\) 層線段左/右端點的最少步數就是一個子問題。

根據我們得到的子問題,我們設狀態 \(dp_{i,[0/1]}\) 表示從 \((1,1)\) 走到第 \(i\) 層線段的左/右端點的最少步數。以從 \(dp_{i-1,0}\)\(dp_{i,1}\) 轉移為例(也就是從上一層線段左端點走到這一層線段右端點),我們首先需要橫向走到這一層線段左端點,將橫座標從 \(L_{i-1}\)

走到 \(L_i\),顯然需要 \(|L_{i-1}-L_i|\) 步,然後從線段左端點走到線段右端點,顯然需要 \(R_i-L_i\) 步,又因為需要從上一層走到這一層,還需要額外的 \(1\) 步。我們得到轉移 \(dp_{i,1}\gets dp_{i-1,0}+|L_{i-1}-L_i|+R_i-L_i+1\)。類似地,我們可以得到從 \(dp_{i-1,[0/1]}\)\(dp_{i,[0/1]}\) 的所有轉移,對於一個目標狀態,把所有能轉移過來的結果取最小值即可。

最終我們還需要從 \(L_n\)\(R_n\) 走到 \((n,n)\),可以按照類似的方法求出 \(ans\gets\min\{dp_{n,0}+n-L_n,dp_{n,1}+n-R_n\}\)

初始狀態就是從 \((1,1)\)\(R_1\) 的距離,或者到 \(R_1\) 再到 \(L_1\) 的距離。

參考程式碼

//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
using namespace std;
typedef long long ll;
const int N = 2e4+5; 

int n, L[N], R[N], dp[N][2];
template<typename T> void chkmin(T &x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T &x, T y) {if(x < y) x = y;}

int main() {
    scanf("%d", &n);
    rep(i, 1, n) scanf("%d%d", &L[i], &R[i]);
    dp[1][0] = 2 * R[1] - L[1] - 1;
    dp[1][1] = R[1] - 1;
    rep(i, 2, n) {
        dp[i][0] = min(dp[i-1][0]+abs(L[i-1]-R[i])+R[i]-L[i], 
                       dp[i-1][1]+abs(R[i-1]-R[i])+R[i]-L[i]) + 1;
        dp[i][1] = min(dp[i-1][0]+abs(L[i-1]-L[i])+R[i]-L[i], 
                       dp[i-1][1]+abs(R[i-1]-L[i])+R[i]-L[i]) + 1;
    }
    int ans = min(dp[n][0]+n-L[n], dp[n][1]+n-R[n]);
    printf("%d\n", ans);
    return 0;
}
博文作者:rui_er。本部落格所有公開(無密碼)博文可根據 CC BY-NC-SA 4.0 協議進行轉載,非公開博文可與作者聯絡獲得許可後轉載。