Google题解
Kickstart2017 RoundB
B.题意: 二维平面上有n个点, 每个点坐标(xi, yi), 权值wi, 问: 在平面上找一点p, 使得 Σwi*max(|X-xi|, |Y-yi|)最小, (X, Y)为p点坐标。求该最小值。
二维空间:
欧几里得距离:d=√(|x1-x2|2+|y1-y2|2)
曼哈顿距离 :d=|x1-x2|+|y1-y2|,到某点的曼哈顿距离为r的点组成一个边长为√2*r的正方形,且边与坐标轴成45度
切比雪夫距离:d=max(|x1-x2|,|y1-y2|),到某点的切比雪夫距离为r的点组成一个边长为2*r的正方形,且边与坐标轴平行
1 /* 2 题解: 赛后看到有不少随机化算法可乱搞过去 3 该题是二维平面邮局距离(参见《算法导论》)的变形 4 5 simple版:一维下, 使得Σwi*|X-xi|最小, 其中所有wi = 1. 答案显然为xi的中位数 6 middle版:我们可以将wi看成有wi个点重合, 权值均为1, 则可套用simple版解法 7 hard版:二维下, 使得 Σwi*(|X-xi| +|Y-yi|)最小. X轴, Y轴分开解即可 8 此题:我们将整个平面旋转45°即可变为hard版. 为什么? 9 10 因为hard中|X-xi| +|Y-yi|表示的边界形状为以(xi, yi)为中心的45°倾斜的正方形 11 而本题max(|X-xi|, |Y-yi|)表示的边界形状为以(xi, yi)为中心的正方形 12 */ 13 14 #include <iostream> 15 #include <algorithm> 16 #include <vector> 17 #include <string> 18 #include <unordered_set> 19 #include <unordered_map> 20 #include <set> 21 #include <map> 22 #include <queue> 23 #include <stack> 24 #include <functional> 25 #include <cmath> 26 #include <string.h> 27 28 using namespace std; 29 using ull = unsigned long long; 30 using ll = long long; 31 using db = double; 32 using PII = pair<int, int>; 33 34 template<typename T> 35 void print_vec(T& container, const std::string& sep = " "){ 36 for (auto& x: container){ 37 std::cout << x << sep; 38 } 39 std::cout << std::endl; 40 } 41 42 template<typename T> 43 void print_map(T& mp){ 44 for(auto& x: mp){ 45 std::cout << x.first << " " << x.second << std::endl; 46 } 47 } 48 49 struct Point{ 50 double x, y, w; 51 void input(){ 52 cin >> x >> y >> w; 53 } 54 double dist(double _x, double _y){ 55 return fabs(x - _x) + fabs(y - _y); 56 } 57 void rotate(){ 58 static double ang = 45.0 / 180 * acos(-1.0); 59 double x1 = x * cos(ang) - y * sin(ang); 60 double y1 = x * sin(ang) + y * cos(ang); 61 x = x1 * sin(ang) ; 62 y = y1 * sin(ang) ; 63 } 64 }; 65 struct Problem{ 66 67 int N; 68 Point p[10010]; 69 void read(){ 70 cin >> N; 71 for (int i = 0; i < N; i++){ 72 p[i].input(); 73 p[i].rotate(); 74 } 75 } 76 77 struct WEIGHT{ 78 double w; 79 double x; 80 bool operator<(const WEIGHT& wt) const{ 81 return x < wt.x; 82 } 83 }; 84 85 double calc(WEIGHT wt[], int n){ 86 sort(wt, wt + n); 87 double ans = 0; 88 double cur_x = wt[0].x; 89 double pre_sum = 0; 90 double suf_sum = 0; 91 for (int i = 0; i < n; i++){ 92 ans += (wt[i].x - cur_x) * wt[i].w; 93 suf_sum += wt[i].w; 94 } 95 double ret = ans; 96 for (int i = 1; i < n; i++){ 97 double nxt_x = wt[i].x; 98 suf_sum -= wt[i-1].w; 99 pre_sum += wt[i-1].w; 100 ans -= suf_sum * (nxt_x - cur_x); 101 ans += pre_sum * (nxt_x - cur_x); 102 cur_x = nxt_x; 103 ret = min(ans, ret); 104 } 105 return ret; 106 } 107 void solve(int ca){ 108 printf("Case #%d: ", ca); 109 110 WEIGHT vx[10010], vy[10010]; 111 for (int i = 0; i < N; i++){ 112 vx[i].x = p[i].x; 113 vx[i].w = p[i].w; 114 vy[i].x = p[i].y; 115 vy[i].w = p[i].w; 116 } 117 double sum = calc(vx, N) + calc(vy, N); 118 printf("%.7f\n", sum); 119 } 120 }; 121 122 int main(){ 123 int T; 124 cin >> T; 125 for (int ca = 1; ca <= T; ca++){ 126 Problem p; 127 p.read(); 128 p.solve(ca); 129 } 130 }
附随机化模拟退火算法:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <climits> 9 #include <stack> 10 #include <queue> 11 #include <ctime> 12 #include <string> 13 #include <iostream> 14 #include <algorithm> 15 using namespace std; 16 17 #define MEM(a,b) memset(a,b,sizeof(a)) 18 #define REP(i,n) for(int i=0;i<(n);++i) 19 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 21 #define MP(a,b) make_pair(a,b) 22 23 typedef long long ll; 24 typedef pair<int,int> pii; 25 const int INF = (1 << 30) - 1; 26 const int MAXN = 10010; 27 const double cof = 0.5; 28 const double eps = 1e-3; 29 const double deta = 0.7; 30 31 int T,N; 32 double X[MAXN],Y[MAXN], W[MAXN]; 33 int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; 34 35 double Dis(double a1,double b1,double a2,double b2){ 36 return max(abs(a1-a2), abs(b1-b2)); 37 } 38 39 double Cal(double a,double b){ 40 double res = Dis(a,b,X[0],Y[0]) * W[0]; 41 FOR(i,1,N - 1) res += Dis(a,b,X[i],Y[i]) * W[i]; 42 return res; 43 } 44 45 int main(){ 46 srand((unsigned)time(NULL)); 47 int a,b; 48 scanf("%d",&T); 49 FOR(tt,1,T){ 50 scanf("%d",&N); 51 REP(i,N) scanf("%lf%lf%lf",&X[i],&Y[i], &W[i]); 52 double ans = -1,ansx,ansy; 53 REP(o,100){ 54 double sx = rand() % (N + 1); 55 double sy = rand() % (N + 1); 56 double res = Cal(sx,sy); 57 double step = N; 58 while(step > eps){ 59 while(1){ 60 bool mov = false; 61 REP(k,4){ 62 double tx = sx + dir[k][0] * step; 63 double ty = sy + dir[k][1] * step; 64 if(tx < -10000 || tx > 10000 || ty < -10000 || ty > 10000) continue; 65 double tmp = Cal(tx,ty); 66 if(tmp < res || (double)rand()/INT_MAX > deta){ 67 res = tmp; 68 sx = tx; 69 sy = ty; 70 mov = true; 71 } 72 } 73 if(mov == false) break; 74 } 75 step *= cof; 76 } 77 if(ans == -1 || res < ans){ 78 ans = res; 79 ansx = sx; 80 ansy = sy; 81 } 82 } 83 printf("Case #%d: %.6lf\n", tt, ans); 84 //printf("The safest point is (%.1f, %.1f).\n",ansx,ansy); 85 } 86 return 0; 87 }
诸神对凡人心生艳羡,厌倦天堂。