F - Hammer 2(ABC273f)
题意
高桥是数轴的起源。高桥想在坐标达到目标X.
此外,还有n墙壁和n锤在数轴上。
在坐标是
Y1…Yn
是类型的墙1…n分别。
最初,高桥无法翻墙。
在坐标Z1…Zn
是类型的锤子1 、2 、…,n,分别。
当他用锤子到达一个坐标时,他得到了锤子。
锤子致力于破坏类型相同的墙. 在他得到锤子i之后,他可以摧毁墙i。
确定他能否达到目标。如果可以,找出他行进的最短距离。
样例
输入
3 10
-2 8 -5
5 -10 3
输出
40
解法 一 dp
一共有2 * n + 2个点(原点, x, 2 * n), 排序,记录位置
dp[i][j][0/1] 表示走过从i 到 j, 当前在i, 或 j 的最小代价
转移从(i, j, 0/1) 到(i - 1, j, 0) 和(i, j + 1, 1) , 再加上距离就行了。
时间O(n2 * 4) 空间同理
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
const int maxn = 4e3 + 10;
typedef long long ll;
typedef pair<int, int> pii;
int n, x;
int p0, px;
int a[maxn], b[maxn];
pii c[maxn];
ll fL[maxn][maxn], fR[maxn][maxn];
ll ans = 1e18;
int py[maxn], pz[maxn];
signed main() {
cin >> n >> x;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<pii> v;
v.push_back({0, n});
v.push_back({x, n + 1});
for (int i = 0; i < n; i++) {
cin >> b[i];
v.push_back({a[i], i});
v.push_back({b[i], ~i});
}
int sz = (int)v.size();
sort(v.begin(), v.end());
for (int i = 0; i < v.size(); i++) {
if (v[i].se >= 0 && v[i].se < n) py[v[i].se] = i;
else if (v[i].se < 0) pz[~v[i].se] = i;
if (v[i].se == n + 1) px = i;
if (v[i].se == n) p0 = i;
}
for (int i = 0; i < 4000; i++) for (int j = 0; j < 4000; j++) fL[i][j] = fR[i][j] = 1e18;
fL[p0][p0] = 0;
fR[p0][p0] = 0;
for (int i = p0; i >= 0; i--) {
for (int j = p0; j < sz; j++) {
if (i <= px && j >= px) {
ans = min(ans, fL[i][j]);
ans = min(ans, fR[i][j]);
}
if (i > 0) {
int ok = 1;
int id = v[i - 1].second;
if (id >= 0 && id < n) if (pz[id] < i || pz[id] > j) ok = 0;
if (ok) fL[i - 1][j] = min(fL[i - 1][j], fL[i][j] + abs(v[i].fi - v[i - 1].fi)),
fL[i - 1][j] = min(fL[i - 1][j], fR[i][j] + abs(v[j].fi - v[i - 1].fi));
}
if (j < sz - 1) {
int ok = 1;
int id = v[j + 1].second;
if (id >= 0 && id < n) if (pz[id] < i || pz[id] > j) ok = 0;
if (ok) fR[i][j + 1] = min(fR[i][j + 1], fL[i][j] + abs(v[i].fi - v[j + 1].fi)),
fR[i][j + 1] = min(fR[i][j + 1], fR[i][j] + abs(v[j].fi - v[j + 1].fi));
}
}
}
if (ans < 1e18) cout << ans << endl;
else cout << -1 << endl;
return 0;
}
解法2 模拟+贪心O(nlogn)7ms
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
const int maxn = 4e3 + 10;
typedef long long ll;
typedef pair<int, int> pii;
int n, x;
int a[maxn], b[maxn];
int sl[maxn], sr[maxn];
int l = -1e9, r = 1e9;
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> x;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
vector<pii> li, ri;
for (int i = 0; i < n; i++) {
if (a[i] < 0 && b[i] < 0 || a[i] > 0 && b[i] > 0) {
if (abs(a[i]) < abs(b[i])) {
if (a[i] > 0) r = min(r, a[i]);
else l = max(l, a[i]);
}
} else {
if (a[i] > 0) ri.push_back({a[i], i});
else li.push_back({a[i], i});
}
}
sort(li.rbegin(), li.rend());
sort(ri.begin(), ri.end());
if (l > x || r < x) {
cout << -1 << endl;
return 0;
}
int mn = 0, mx = 0;
for (auto p:ri) sr[p.se] = min(mn, b[p.se]), mn = min(sr[p.se], mn);
for (auto p:li) sl[p.se] = max(mx, b[p.se]), mx = max(sl[p.se], mx);
int c = x, nc;
ll ans = 0;
set<int> mp;
while(c != 0) {
if (mp.count(c)) {
cout << -1 << endl;
return 0;
}
mp.insert(c);
if (c > 0) {
auto it = lower_bound(ri.begin(), ri.end(), make_pair(c, 0));
if (it != ri.begin()) {
nc = sr[prev(it)->second];
} else nc = 0;
} else {
auto it = upper_bound(li.rbegin(), li.rend(), make_pair(c, 0));
if (it != li.rend()) {
nc = sl[it->second];
} else nc = 0;
}
ans += abs(c - nc);
c = nc;
}
cout << ans << endl;
return 0;
}