URAL 1956 Fire Signals

题意:

求一条直线满足,所有平面上点到这条直线的距离之和最小,点数n<1000,输出最小的距离纸盒之和。



经过长时间的YY,得出经过平面上的两个点的直线包含最优解。可以由  dis = |a*x+b*y+c|/sqrt(a*a+b*b) 得到。

因为在直线一侧的点 dis 的正负号是相同的,可以利用这一点。先枚举一点,另外的点按到这点的角度排序,然后扫描枚举另一点,维护两边的点的x之和,y之和,用abs(sum(a*x0+b*y0+c) -abs(a*x1+b*y1+c))/sqrt(a*a+b*b)  跟新答案,x0,y0是直线一边点的x,y的和,x1,y1是另一边的。

#include<stdio.h>
#include<vector>
#include<map>
#include<algorithm>
#include<math.h>
#include<queue>
#include<iostream>
#define clr(x,y) memset(x,y,sizeof(x))
#define ll long long
using namespace std;

const double pii = acos(-1.0);
const double ans = 1e20;

int n;
ll x[1005],y[1005],mx,my;
struct item{
    double k;
    ll x,y;
    item(){}
    item(double k,ll x,ll y):k(k),x(x),y(y){}
    bool operator<(const item b)const{
        return k<b.k;
    }
}p[1005];
bool dcmp(double a,double b){
    if(fabs(a-b)<1e-8) return 1;
    else return 0;
}

double dis(int s,int t){
    double ds = p[s].k;
    double dt = p[t].k;

    if(s<=t) return dt-ds;
    else return dt-ds+2*pii;
}

double get(double a,double b,ll sx,ll sy){
    if(dcmp(a,0) && dcmp(b,0)) return 1e18;
    ll tx = sx - (mx - sx);
    ll ty = sy - (my - sy);
    return fabs(a*tx+b*ty)/sqrt(a*a+b*b);
}

int main(){
//    freopen("/home/zyc/Documents/Code/cpp/in","r",stdin);
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        cin>>x[i]>>y[i];
    }
    bool flag = true;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++)
            if(x[i]!=x[j]||y[i]!=y[j])
                flag = false;
    }
    if(flag){
        puts("0");
        return 0;
    }

    double ans = 1e18;
    for(int i=0;i<n;i++){
        mx = 0;
        my = 0;
        for(int j=0;j<n;j++){
            ll nx = x[j] - x[i];
            ll ny = y[j] - y[i];
            mx += nx;
            my += ny;
            double nk = atan2(ny,nx);
            p[j] = item(nk,nx,ny);
        }
        sort(p,p+n);
        int r = 0;
        ll sx=0,sy=0;
        while(dis(0,r) <= pii){
            sx += p[r].x;
            sy += p[r].y;
            r = (r+1)%n;
            if(r==0) break;
        }
        for(int l = 0;l<n;l++){
            if(l!=0){
                while(dis(l,r)<=pii){
                    sx += p[r].x;
                    sy += p[r].y;
                    r =(r+1)%n;
                    if(r==l) break;
                }
            }

            ans = min(ans,get(-p[l].y,p[l].x,sx,sy));
            sx -= p[l].x;
            sy -= p[l].y;
        }
    }
    printf("%.10lf\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值