大意: 平面三个点A,B,C, 两人在A,B处, 垃圾桶在C处, 有n个瓶子, 求把两人将瓶子全扔进垃圾桶的最短距离和的最小值
考虑每个人的行走路线, 一定是这种形式 |Aa1|+|a1C|+|Ca2|+|a2C|+|Ca3|+|a3C|+...
也就是说除了第一个拿的瓶子外, 其余瓶子贡献都为2|aiC|
所以就得到答案为 $\min\limits_{x!=y}(|Aa_x|-|a_xC|+|Ba_y|-|a_yC|)+2\sum\limits|a_iC|$
这样遍历一下求出$|Aa_x|-|a_xC|$和$|Ba_y|-|a_yC|$最小值即可
复杂度O(n)
#include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #define PER(i,a,n) for(int i=n;i>=a;--i) #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; const int N = 4e5+10, INF = 0x3f3f3f3f; struct Point { int x, y; Point () {} Point (int x, int y) :x(x),y(y) {} void rd() { scanf("%d%d", &x, &y); } Point operator - (Point p) { return Point(x-p.x,y-p.y); } double dis() { return sqrt(x*x+y*y); } } A, B, C, a[N]; double calc(Point x, int p) { return (x-a[p]).dis()-(a[p]-C).dis(); } int n, s1[N], s2[N]; int main() { A.rd(),B.rd(),C.rd(); scanf("%d", &n); double ans = 0; REP(i,1,n) { a[i].rd(); ans += (a[i]-C).dis()*2; s1[++*s1] = i; PER(i,2,*s1) if (calc(A,s1[i])<calc(A,s1[i-1])) swap(s1[i],s1[i-1]); *s1 = min(*s1, 2); s2[++*s2] = i; PER(i,2,*s2) if (calc(B,s2[i])<calc(B,s2[i-1])) swap(s2[i],s2[i-1]); *s2 = min(*s2, 2); } double mi = 1e20; REP(i,1,*s1) REP(j,1,*s2) if (s1[i]!=s2[j]) { mi = min(mi, calc(A,s1[i])+calc(B,s2[j])); } printf("%.12lf\n", ans+mi); }