Codeforces 329E Evil
見た目が解けるものに見えないが、実は解ける。見た目とのギャップがすごい。
- 問題概要
平面上にN点与えられるので、N点全てを一回ずつ使うサイクルを作って、隣り合う2点間のマンハッタン距離の総和を最大化せよ。
- 解法
x座標の中央値とy座標の中央値で切って領域を4つに分ける。全部の辺がこの対角線の領域同士を結んでいれば、無条件で最大。
それができない場合、まずNの偶奇で場合分けする。
- Nが偶数のとき
左側同士を結ぶ辺と右側同士を結ぶ辺を1本ずつ、あるいは上側同士を結ぶ辺と下側同士を結ぶ辺を1本ずつはる必要がある。両方の場合について試してminをとる。「この2点を結ぶとどれくらい損するか」みたいなのを考えると、どこに辺をはればいいかは割と簡単にわかる。
- Nが奇数で、(x座標の中央値,y座標の中央値)という点がある場合
上と大体同様だが、左側 or 右側 or 上側 or 下側同士を結ぶ辺のうちひとつをはればいい。
- Nが奇数で、(x座標の中央値,y座標の中央値)という点がない場合
上記の上界を達成するサイクルが構成できる。
以上の場合分けをすると通る。面白い。
#include<stdio.h> #include<vector> #include<algorithm> #include<math.h> #include<stdlib.h> using namespace std; typedef long long ll; typedef pair<ll, ll>pii; #define INF 1000000000000000000LL int main() { int num; scanf("%d", &num); vector<pii>vx, vy; for (int i = 0; i < num; i++) { int za, zb; scanf("%d%d", &za, &zb); vx.push_back(make_pair(za, zb)); vy.push_back(make_pair(zb, za)); } sort(vx.begin(), vx.end()); sort(vy.begin(), vy.end()); ll mx = vx[vx.size() / 2].first, my = vy[vy.size() / 2].first; ll sx = 0, sy = 0; for (int i = 0; i < vx.size(); i++)sx += abs(vx[i].first - mx); for (int i = 0; i < vy.size(); i++)sy += abs(vy[i].first - my); if (num % 2 == 0) { int cnt = 0; for (int i = 0; i < num / 2; i++)if (make_pair(vx[i].second, vx[i].first) < vy[num / 2])cnt++; if (cnt == 0 || cnt == num / 2) { printf("%I64d\n", (sx + sy) * 2); } else { ll m1 = INF, m2 = INF, m3 = INF, m4 = INF; for (int i = 0; i < num / 2; i++)m1 = min(m1, mx - vx[i].first); for (int i = num / 2; i < num; i++)m2 = min(m2, vx[i].first - mx); for (int i = 0; i < num / 2; i++)m3 = min(m3, my - vy[i].first); for (int i = num / 2; i < num; i++)m4 = min(m4, vy[i].first - my); printf("%I64d\n", (sx + sy) * 2 - min(m1 + m2, m3 + m4) * 2); } } else { if (make_pair(vx[num / 2].second, vx[num / 2].first) != vy[num / 2]) { printf("%I64d\n", (sx + sy) * 2); } else { int cnt = 0; for (int i = 0; i < num / 2; i++)if (make_pair(vx[i].second, vx[i].first) < vy[num / 2])cnt++; if (cnt == 0 || cnt == num / 2) { printf("%I64d\n", (sx + sy) * 2); } else { ll m1 = INF, m2 = INF, m3 = INF, m4 = INF; for (int i = 0; i < num / 2; i++)m1 = min(m1, mx - vx[i].first); for (int i = num / 2 + 1; i < num; i++)m2 = min(m2, vx[i].first - mx); for (int i = 0; i < num / 2; i++)m3 = min(m3, my - vy[i].first); for (int i = num / 2 + 1; i < num; i++)m4 = min(m4, vy[i].first - my); printf("%I64d\n", (sx + sy) * 2 - min(min(m1, m2), min(m3, m4)) * 2); } } } }