Problem :
有兩個長度爲n的隊列過安檢,每一個人有一個特徵值。若是兩個隊列中的第一我的的特徵值之差小於等於k,那麼一次只能檢查其中一我的,不然一次能夠檢查兩我的。每次檢查花費1的世時間。問最後檢查完全部人以後所須要的時間。(n <= 60000, k <= 10)(3s時限)
Solution :
容易想到一個dp方程,dp[i][j]表示當前檢查到a隊列第i我的,b隊列第j我的。
dp[i][j] = dp[i - 1][j - 1] + 1 ( abs(a[i] - b[j] > k)
dp[i][j] = min(dp[i - 1][j] + dp[i][j - 1]) + 1 ( abs(a[i] - b[j] <= k)
從二維平面的角度考慮,每一個點的決策至關因而從左、下、左下三個方向轉移過來。
注意到 k<=10,即在多數狀況下狀態由左下方轉移過來,少數點來自於左或下。
所以能夠對於每一個對角線維護一個dp值,對於每一個須要從左或下轉移的關鍵點進行處理。其狀態能夠由下方或者左方對應的對角線上的狀態轉移過來。
最後若終點不是關鍵點,特殊處理一下。php
#include <iostream> #include <algorithm> using namespace std; const int INF = 1e8 + 7; const int N = 600008; int dp[N << 1], f[N << 1]; int a[N], b[N], ref[N]; int sol[N]; int n, k; void init() { cin >> n >> k; for (int i = 1; i <= n; ++i) cin >> a[i]; for (int j = 1; j <= n; ++j) { cin >> b[j]; ref[b[j]] = j; } } void solve() { for (int i = - n - 1; i <= n + 1; ++i) dp[i + N] = INF; for (int i = 0; i <= n; ++i) dp[i + N] = i, f[i + N] = 0; for (int i = 0; i <= n; ++i) dp[-i + N] = i, f[-i + N] = i; for (int i = 1; i <= n; ++i) { int tot = 0; for (int j = a[i] - k; j <= a[i] + k; ++j) { if (j <= 0) continue; if (j > n) continue; sol[++tot] = ref[j]; } sort(sol + 1, sol + tot + 1); for (int j = 1; j <= tot; ++j) { int tmp = sol[j] - i + N; dp[tmp] = min(dp[tmp + 1] + i - 1 - f[tmp + 1] + 1, dp[tmp - 1] + i - f[tmp - 1] + 1); f[tmp] = i; } } if (abs(a[n] - b[n]) > k) dp[N] = dp[N] + n - f[N]; cout << dp[N] << endl; } int main() { cin.sync_with_stdio(0); int T; cin >> T; while (T--) { init(); solve(); } }