题目大意
给定一维直线上的N个点,每个点有一个颜色标记(红、绿、蓝),现在要连边,一条边的代价为两点距离。现求最小总代价,使红、绿点连通,并且蓝、绿点连通。
题目分析
考虑到,红、蓝点连线并无意义,所以可忽略;另外考虑假如出现了连续三个点,颜色分别为红绿红,那么红和红之间连线一定不必红-绿-红这样连线优。所以,以绿点为分割点,分成若干个区间单独考虑即可。
对于两个连续的绿点之间的区间,有两种连线方式:(1)连接两个绿点,再以从这两个绿点为端点,连出两条线,分别连接中间的红点和蓝点,接着分别去掉这两条线的最长一个区间;(2)直接从这两个绿点为端点,连出两条线,分别连接中间的红点和蓝点。
分别统计两种连线方式的代价,选最优即可。
代码写得很丑,要看漂亮代码请戳这:by aaaaajack
source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <iostream> #include <string> #include <algorithm> #include <vector> using namespace std; typedef long long ll; vector<ll> v0, v1, v2; ll ans; void add(vector<ll> &v){ if (v.empty()) return; ans += max(0LL, v0.front() - v.front()) + max(0LL, v.back() - v0.back()); }
vector<ll>::iterator LB(vector<ll> &v, ll x) { return lower_bound(v.begin(), v.end(), x); }
ll calc(ll l, ll r) { if (LB(v1, l) == LB(v1, r) && LB(v2, l) == LB(v2, r)) return r - l; ll x = l, max_interval = 0; for (auto it = LB(v1, l); it != v1.end() && (*it) < r; it++) { max_interval = max(max_interval, (*it) - x); x = *it; } max_interval = max(max_interval, r - x); ll ans1 = 3 * (r - l) - max_interval; x = l; max_interval = 0;
for (auto it = LB(v2, l); it != v2.end() && (*it) < r; it++) { max_interval = max(max_interval, (*it) - x); x = *it; } max_interval = max(max_interval, r - x); ans1 -= max_interval; return min(ans1, 2 * (r - l)); }
int main() { ios::sync_with_stdio(false); cin.tie(0); ll N; cin >> N; for (ll i = 0; i < N; i++) { ll x; string s; cin >> x >> s; if (s[0] == 'G') v0.push_back(x); else if (s[0] == 'B') v1.push_back(x); else v2.push_back(x); } if (v0.size() <= 1) { if (!v1.empty()) ans += v1.back() - v1.front(); if (!v2.empty()) ans += v2.back() - v2.front(); } else { add(v1); add(v2); for (ll i = 0; i < v0.size() - 1; i++) { ans += calc(v0[i], v0[i + 1]); } } cout << ans << endl; }
|
Last updated: