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(); } }