生日派对分饼、中位数(二分)

描述

给定N个数字,X1,X2,……XN,让我们计算每一对数的差值:|Xi-Xj| (1 ≤ i < j ≤ N),可以获得C(N,2)个数的中位数(即N*(N-1)/2个)

输入格式

在每个测试用例中,N将在第一行给出。然后Ñ编号给予,表示X 1,X 2,…,X Ñ,( Xi ≤ 1,000,000,000 , 3 ≤ N ≤ 1,00,000 )

输出格式

在单独的行中输出中值

样例输入

4
1 3 2 4
3
1 10 2

样例输出

1
8

题意

N个数两两做差会得到C(N,2)个差值,求这些差值的中位数

思路

第一眼看到的想法就是把所有的差值求出,再找中位数,但一看数据N最大可以去到100000两两求差值N2必超时。中位数前后的前后的元素个数要么一样多(N为奇数),要么差一个(N为偶数),于是我们可以对这个中位数的值进行二分,满足条件的就是这组数据的中位数,那么问题来了怎么求差值,都求出来肯定不行,会超时,那我们就一边求一边判断(记得这个时候先把数组排个序,差值一旦大于二分出来的mid,就不用再求后面的了,越来越大了呀)把小于mid的进行计数,最后判断这个mid符不符合中位数的条件,然后就是二分的老套路了

AC代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int a[100010];
int n,ans;
int judge(int mid)
{
     int cnt=0,t=0;
     for(int i=0;i<n;i++)
     {
          while(t<n&&a[t]-a[i]<mid)//求差值判断是不是在mid的前面
              t++;
          cnt+=t-i-1;
     }
     if(cnt>=ans)//将计得的数与中位数的条件比较看能否成为中位数
          return 1;
     else
          return 0;
}
int main()
{
     while(~scanf("%d",&n))
     {
         for(int i=0;i<n;i++)
             scanf("%d",&a[i]);
         int m=n*(n-1)/2;
         //两种情况下的中位数位置
         if(m%2==0)
            ans=m/2;
         else
            ans=(m+1)/2;
         sort(a,a+n);//排序,为judge当中的while循环准备的,能够减少循环次数
         int left=0,mid,right=a[n-1]-a[0],res;
         while(right-left>=0)
         {
               mid=(right+left)/2;
               if(judge(mid))
                    right=mid-1;  
               else
                    left=mid+1;
         }
         printf("%d\n",right);
      }
}

描述

在我的生日派对上有N个饼,每个饼的大小不同,有许多朋友来参加我的生日派对,他们每一个人都得到了一块饼,如果其中有一个的饼比另一个人的大,他们就会抱怨,所以所有的饼应该分成相同的大小,当然了,我自己也要一块饼,而且那块也应该是同样的大小,我们所有人可能得到的最大尺寸的饼是多少?我做的饼都是圆柱形的,高度都是1,但是半径可以不一样。

输入格式

一行为正整数:测试用例的组数
然后对于每个测试用例:一行带有两个整数N,F带有1<=N,F<=10000表示饼的数目和好友的数目。
一行N个整数ri,1<=ri<=10000:饼的半径。

输出格式

对于每个测试用例,输出一行具有最大可能的数值,这样我和我的朋友都可以得到一块大小为V的饼。答案应该以浮点数给出,绝对误差最多为10^(-7)保留6位小数。

样例输入

3
3 3
4 3 3
1 24
5
10 5
1 4 2 3 4 5 6 5 4 2

样例输出

25.132741
3.141593
50.265482

题意

N+1个人分饼,饼不止一个,每个人的饼要一样大,求每个人分的饼最大有多大

思路

这个类似之前写的那个切绳子,二分过程一样,两道题都卡了精度,这里是给出了绝对误差的最大值,一开始没能理解这个,哪里会有误差存在呢,其实就是二分越往后分right和left靠的越来越近,这个地方能出现误差,所以我们可以让right和left的差值小于10-7时结束while。还有就是这题精度比较高要六位小数,所以Π的值还是搞精确点用acos(-1)来表示。

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define PI acos(-1)
using namespace std;
double v[100010];  
int N,F;
int cut(double mid)
{
 	int cnt=0;
 	for(int i=0;i<N;i++)
 	{
  	    cnt+=(int)(v[i]/mid);//记得转int
    	}
 	if(cnt>=F)
     	    return 1;
 	else
     	    return 0;
}
int main()
{
 	int T;
 	cin>>T;
 	while(T--)
 	{
 		double R,maxn=0;
  		cin>>N>>F;
  		F++;//加上自己
  		for(int i=0;i<N;i++)
  		{
   			cin>>R;
   			v[i]=R*R*PI;
   			maxn=max(maxn,v[i]);
 		 }
  		double left=0,mid,right=maxn;
  		while(right-left>=1e-7)//允许误差范围
  		{
   			mid=(right+left)/2.0;
   			if(cut(mid))
    			left=mid;
   			else
      			 right=mid;
  		}
  		printf("%.6lf\n",mid);//保留六位小数
 	}
 	return 0}
发布了32 篇原创文章 · 获赞 5 · 访问量 894

猜你喜欢

转载自blog.csdn.net/LebronGod/article/details/104457559