题目: 有2N个实数,不重复每次选取两个数,一个向下取整,另一个向上取整, 求变化前后和值之差的 最小值。
分析: 这种题没什么特定的算法,比赛时看RP, 不是容易想到方法。
可以这样考虑这个问题, 设实数对应的小数部分为X, 若向下取整 变化量为 -Xi, 若向上取整变化量为 +(1-Xj) ,总变化量为M*1+Sigma{ X } [ M是1的个数 ] (当然整数向上取整不变)
惊人的发现小数部分的变化情况都是去负号, 这样我们只需对所有的小数部分求和,为定值, 而决定变化量的是 1 的个数,(只有对非正数向上取整才有1)
比如下面两种情况:
(1) 1.2 2.0 3.0 4.0 (1最多一个,最少零个)
(2) 1.2 2.3 3.4 5.0 (1最多两个,最少一个)
可以得到规律:1的个数总是在某个区间变化, 并可以取其中的任意值。 接下来只需枚举这些可能, 求变化量的最小值即可。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long LL;
const double eps=1e-20;
double a[5000];
int main()
{
double x,sum=0;
int N,cnt=0;
cin>>N;
for(int i=0;i<N*2;i++){
cin>>x;
double t=x-floor(x);
sum += t;
if(t>eps) cnt++;
}
double ans=1e10;
int u,v;
if(cnt<=N){
u=0; v=cnt;
}
else {
u=cnt-N; v=N;
}
for(int i=u;i<=v;i++){
ans=min(ans,abs(i-sum));
}
printf("%.3f\n",ans);
return 0;
}