hdu5073 -- 贪心(2014西安现场赛)


大意:给出n个在一条直线上的点,允许移动其中的k个点,求移动k个点后,其余所有点到重心的距离的平方和最小。


思路:首先,5w个点,暴力解会超时。可以看出,k个点肯定是移动到最后 的重心处,所以这k个点就不用再考虑了。关于重心,因为所有点的质量都是1,因此将所有点的平均值就是重心。接下来就是解这一题的关键了,留下哪些点才能使公式 I 的值最小,试想一下,如果将n个点排序,那么,取连续的n-k个点的 I 的值肯定比不连续的小,既然这样的话,只需要从区间1~n-k枚举到区间k+1~n这k个长度为n-k的区间即可


还有一点就是求公式 I 的时候,暴力解复杂度很大,不妨可以将公式推一下,然后化简,最终可以得到O(1)的求解复杂度


I = d1^2 + d2^2 + d3^2 +  .... + dn^2

  = (X1-X0)^2 + (X2-X0)^2 + .... + (Xn-X0)^2

  = (X1^2+X2^2+X3^2+.....+Xn^2) - 2X0(X1+X2+....+Xn) + nX0^2

(X0表示重心,Xi表示第i个点)


从上面化简的结果可以看出,X1^2+X2^2+X3^2+.....+Xn^2 可以用一个sum2数组存,sum2[n]表示前n个x^2和,同理用sum[n]表示前n个x和,那么O(1)的求解复杂度就出来了


最后还有一点要注意的是,题目说误差要小于1e-9,那么结果要取到小数点后十位


AC代码:

#include <cstdio>  
#include <algorithm>  
#include <cstring>  
#include <cmath>  
#include <iostream>  
using namespace std;  
double arr[50005],sum[50005],sum2[50005];
int main()  
{  
	//freopen("input.txt","r",stdin);
    int T,i,n,k,m;
    double minm,aver,temp;
    scanf("%d",&T);  
    while(T--) {
    	sum[0]=sum2[0]=0;
    	scanf("%d %d",&n,&k);
		for(int i=1;i<=n;i++){
			scanf("%lf",&arr[i]);
		}
		sort(arr+1,arr+n+1);  
		for(int i=1;i<=n;i++){
			sum[i]=sum[i-1]+arr[i];
			sum2[i]=sum2[i-1]+arr[i]*arr[i];		
		}
		m=n-k;
		if(m==0){
			printf("0\n");
		} else{
			aver=sum[m]/m;
			minm=sum2[m]+m*aver*aver-2*aver*sum[m];
			for(int i=m+1;i<=n;i++){
				aver=(sum[i]-sum[i-m])/m;
				temp=sum2[i]-sum2[i-m]+m*aver*aver-2*aver*(sum[i]-sum[i-m]);
				if(temp<minm){
					minm=temp;
				}
			}
			printf("%.10f\n",minm);
		}
    }  
    return 0;  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值