最大间隙问题
时间限制(普通/Java) :
4000 MS/ 12000 MS 运行内存限制 : 65536 KByte
总提交 : 227 测试通过 : 69
若x[i]=minx,则被分到第1个桶中(minx即为桶1的下界);
总提交 : 227 测试通过 : 69
比赛描述
最大间隙问题:给定n 个实数X1 , X2 ,Xn,求这n个数在实轴上相邻2个数之间的最大差值。假设对任何实数的下取整函数耗时O(1),设计解最大间隙问题的线性时间算法。
对于给定的n 个实数X1,X2,..., Xn,编程计算它们的最大间隙。
输入
输入数据的第1行有1个正整数n。接下来的1行中有n个实X1 ,X2 ,..., Xn
输出
输出最大间隙数
样例输入
5
2.3 3.1 7.5 1.5 6.3
样例输出
3.2
题目来源
算法设计与实验题解
思路:(参考别人的)
1. 找到n个数据中最大和最小数据maxx和minx;
2. 用n-2个点等分区间[minx,maxx],即将[minx,maxx]等分为n-1个区间(前闭后开区间),将这些区间看做桶,编号为1,2,...,n-2,n-1,且桶i的上界和桶i+1的下届相同,即每个桶的大小相同;
每个桶的大小为: dblAvrGap=(maxx-minx)/(n-1)
实际上,这些桶的边界就构成了一个等差数列(首项为minx,公差d=dblAvrGap),且人为将minx放入第1个桶,将maxx放入第n-1个桶。
编程实现中,用以下数据结果存放有关桶的数据:
int *count=new int[n]; //实际分到每个桶的数据个数
double *low=new double[n]; //实际分到每个桶的最小数据
double *high=new double[n]; //实际分到每个桶的最大数据
3. 将n个数放入n-1个桶中:
3.1 按如下规则将x[i]分配到某个桶(编号index)中: index=int((x[i]-minx)/dblAvrGap)+1;
若x[i]=minx,则被分到第1个桶中(minx即为桶1的下界);
若x[i]=桶j的下界(也是桶j-1的上界),则被分到桶j中(j>=1);
若x[i]=maxx,则被分到桶n中(max为桶n的下界桶n-1的上界),但没有桶n,解决办法:
可人为将其移入桶n-1中或者再加一个桶,这并不影响求其最大间隙;
3.2 调整分到该桶的最大最小数据;
4. 求最大间隙:
除最大最小数据maxx和minx以外的n-2个数据被放入n-1个桶中,由抽屉原理可知至少有一个桶是空的;
又因每个桶的大小相同,所以最大间隙不会在同一桶中出现;
一定是某个桶的上界(dblHigh)和其后某个桶的下界(dblLow)之间隙,且该两桶之间的桶(即编号在该两桶编号之间的桶)一定是空桶;
即最大间隙在桶i的上界和桶j的下界之间产生(j>=i+1);
代码:
#include<iostream>
using namespace std;
//函数模板求最小值
template<class T>
int mini(int n, T* x)
{
T tmp = x[1];
int k=1;
for (int i = 1; i <= n; i++)
{
if (x[i] < tmp)
{
tmp = x[i];
k = i;
}
}
return k;
}
//函数模板求最大值
template<class T>
int maxi(int n, T* x)
{
T tmp = x[1];
int k = 1;
for (int i = 1; i <= n; i++)
{
if (x[i] > tmp)
{
tmp = x[i];
k = i;
}
}
return k;
}
//算法
double maxgap(int n, double* x)
{
double minx = x[mini(n, x)];
double maxx = x[maxi(n, x)];
//用n-2个等间距点分割区间[minx, maxx]
//产生n-1个桶,每个桶i中用high[i]和low[i]
//分别存储分配给桶i的数中最大数和最小数
int* ncount = new int[n + 1];
double* low = new double[n + 1];
double* high= new double[n + 1];
//桶初始化
for (int i = 1; i <= n - 1; i++)
{
ncount[i] = 0;
low[i] = maxx;
high[i] = minx;
}
//将n个数置于n-1个桶中
for (int i = 1; i <= n; i++)
{
int bucket = int((n-1) * (x[i]-minx) / (maxx-minx)) + 1;
ncount[bucket]++;
if (x[i] < low[bucket]) low[bucket] = x[i];
if (x[i] > high[bucket]) high[bucket] = x[i];
}
//此时,除了maxx和minx外的n-2个数被置于n-1个桶中。
//由抽屉原理即知,至少有一个桶是空的。
//这意味着最大间隙不会出现在同一桶中的两个数之间。
//对每一个桶做一次线性扫描即可找出最大间隙。
double tmp = 0;
double left = high[1];
for (int i = 2; i <= n; i++)
{
if (ncount[i])
{
double gap = low[i] - left;
if (gap > tmp) tmp = gap;
left = high[i];
}
}
return tmp;
}
int main()
{
//进行输入
int num;
cin >> num;
double* pdArr = new double[num+1];
for (int i = 1; i <= num; i++)
{
cin >> pdArr[i];
}
cout << maxgap(num, pdArr)<<endl;
system("pause");
return 0;
}
总结:
本程序使用了桶排序思想,建立桶,但分别用三个数组为桶分配最大值,最小值和统计个数,值得一提的是鸽笼原理的使用,因为如果n-2个数分配到n-1个桶里面,一定是最大间隙出现在不同桶,因为中间间隔一个桶的长度,致使必须是分开的桶中相邻数据存在最大间隙。但不能忽略首尾情况。也就是说把n个数分配到n个桶里面,第n个桶只装边界值,因为每个桶利用公式 int((n-1) * (x[i]-minx) / (maxx-minx)) + 1。结果确定的区间是[ ),左开右闭,必须多设置一个装最大边界值的桶。原书作者出错了,忽略了边界情况,如上红体标出,对第n个桶的边缘也需要检测gap大小。