题目:
时间限制:2000ms
单点时限:1000ms
内存限制:256MB
描述
需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上。
网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方。
网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离)。
在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价。
输入
第一行为一个整数T,表示数据组数。
每组数据第一行为四个整数:N, M, A, B。
接下来的A+B行每行两个整数x, y,代表一个坐标,前A行表示各用户的坐标,后B行表示各通讯公司的坐标。
输出
对于每组数据输出一行"Case #X: Y",X代表数据编号(从1开始),Y代表所求最小代价。
数据范围
1 ≤ T ≤ 20
1 ≤ x ≤ N
1 ≤ y ≤ M
1 ≤ B ≤ 100
小数据
1 ≤ N, M ≤ 100
1 ≤ A ≤ 100
大数据
1 ≤ N, M ≤ 107
1 ≤ A ≤ 1000
- 样例输入
2 3 3 4 1 1 2 2 1 2 3 3 2 2 2 4 4 4 2 1 2 2 4 3 1 4 3 1 4 1 3
样例输出
Case #1: 4 Case #2: 13
本题采用全查找会超时。
我的答案:
#include <iostream> #include <vector> using namespace std; struct POINT{ int x; int y; }; int getEuclideanDistance(int x, int y){ return x*x + y*y; } int getManhattanDistance(int x, int y){ return abs(x) + abs(y); } int main(void){ int dataNum; cin >> dataNum; vector<int> answer; for (int i = 0; i < dataNum; i++){ int xmax, ymax, userNum, companyNum; POINT users[1000] = { 0 }; POINT companies[1000] = { 0 }; cin >> xmax >> ymax >> userNum >> companyNum; int xi = xmax, xj = 0, yi = ymax, yj = 0; for (int i = 0; i < userNum; ++i){ cin >> users[i].x >> users[i].y; if (users[i].x>xj) xj = users[i].x; if (users[i].x < xi) xi = users[i].x; if (users[i].y>yj) yj = users[i].y; if (users[i].y < yi) yi = users[i].y; } for (int i = 0; i < companyNum; ++i){ cin >> companies[i].x >> companies[i].y; if (companies[i].x>xj) xj = companies[i].x; if (companies[i].x < xi) xi = companies[i].x; if (companies[i].y>yj) yj = companies[i].y; if (companies[i].y < yi) yi = companies[i].y; } int minlen = -1; for (int x = xi; x <= xj; ++x){ for (int y = yi; y <= yj; ++y){ int olen = 0; for (int i = 0; i < userNum; ++i) olen += getEuclideanDistance(x - users[i].x, y - users[i].y); int mlen = -1; for (int i = 0; i < companyNum; ++i){ if (mlen == -1 || mlen>(abs(x - companies[i].x) + abs(y - companies[i].y))){ mlen = getManhattanDistance(x - companies[i].x, y - companies[i].y); } } if (minlen == -1 || minlen>(olen+mlen)) minlen = olen + mlen; } } answer.push_back(minlen); } for (int i = 0; i < dataNum; i++) cout << "Case #" << i + 1 << ": " << answer[i] << endl; return 0; }
最优答案:
#include <iostream> #include <cstdio> #include <cstring> #include <map> #include <algorithm> using namespace std; #define mxn 200005 #define LL long long #define MP make_pair #define REP(i, a, b) for (int i = a; i <= b; ++i) #define FOR(i, a, b) for (int i = a; i < b; ++i) int dx[] = {0, 0, 0, 1, 1, 1, -1, -1, -1}; int dy[] = {0, 1, -1, 0, 1, -1, 0, 1, -1}; LL ABS(LL x) { return x < 0 ? -x : x; } struct point { LL x, y; point(){}; point(LL x, LL y):x(x),y(y){} point operator - (const point& b) const { return point(x - b.x, y - b.y); } void input() { cin >> x >> y; } LL dis() { return x * x + y * y; } LL len() { return ABS(x) + ABS(y); } }A[1005], B[105]; LL cal(point o, int a, int b) { LL ret = ~0uLL >> 1; REP(i, 1, b) ret = min(ret, (o - B[i]).len()); REP(i, 1, a) ret += (o - A[i]).dis(); return ret; } int main() { int t, n, m, a, b, cas = 0; cin >> t; while (t--) { cin >> n >> m >> a >> b; REP(i, 1, a) A[i].input(); REP(i, 1, b) B[i].input(); LL ans = ~0uLL >> 1, x = 0, y = 0; REP(i, 1, a) x += A[i].x, y += A[i].y; x /= a, y /= a; FOR(k, 0, 9) { LL tx = x + dx[k], ty = y + dy[k]; if (tx < 1 || tx > n || ty < 1 || ty > m) continue; ans = min(ans, cal(point(tx, ty), a, b)); } cout << "Case #" << ++cas << ": " << ans << endl; } return 0; }
“If you give someone a program, you will frustrate them for a day; if you teach them how to program, you will frustrate them for a lifetime.”