7-4 三足鼎立 (25 分)(每日一练)

7-4 三足鼎立 (25 分)

目录

输入格式:

输出格式:

输入样例:

输出样例:

样例解释:

题意理解:

二分法查找原理

具体思路:

代码


当三个国家中的任何两国实力之和都大于第三国的时候,这三个国家互相结盟就呈“三足鼎立”之势,这种状态是最稳定的。

现已知本国的实力值,又给出 n 个其他国家的实力值。我们需要从这 n 个国家中找 2 个结盟,以成三足鼎立。有多少种选择呢?

输入格式:

输入首先在第一行给出 2 个正整数 n(2≤n≤105)和 P(≤109),分别为其他国家的个数、以及本国的实力值。随后一行给出 n 个正整数,表示n 个其他国家的实力值。每个数值不超过 109,数字间以空格分隔。

输出格式:

在一行中输出本国结盟选择的个数。

输入样例:

7 30
42 16 2 51 92 27 35

结尾无空行

输出样例:

9

结尾无空行

样例解释:

能联合的另外 2 个国家的 9 种选择分别为: 

{16, 27}, {16, 35}, {16, 42}, {27, 35}, {27, 42}, {27, 51}, {35, 42}, {35, 51}, {42, 51}

1.题意理解:任意两个数之和大于第三个数---->联想三角形的三边关系
         也就是说只要这三个数满足是三角形的三边关系就一定满足任意两个数之和大于第三个数
        而满足任意两个数之和大于第三个数就一定满足为三角形
        判断三条边是否能组成三角形的所有理由,一个是:三角形内任意2边之和大于第3边,能满足这个就够了.
        但:(三角形内任意2边之差小于第3边)这个也算,
  
  2.做题思路:一开始我用暴力破解:即一个循环输入,再来一个嵌套循环遍历数组找出符合的其他两个国家,
                 但是本题数据过大,运行会超时。代码如下:

#include <stdio.h>
int main() {
    long long n,p,i,j,sum=0;
    scanf("%lld %lld",&n,&p);
    if(n>=2&&n<=100000) {
        long long a[n];
        for(i=0; i<n; i++) {
            scanf("%lld",&a[i]);
        }
        for(i=0; i<=p-2; i++) {
            for(j=i+1; j<n; j++) {
                if(a[i]+a[j]>p&&a[i]+p>a[j]&&a[j]+p>a[i]) {
                    sum++;
                }
            }
        }
        printf("%lld",sum);
    }
    return 0;
} 


于是整个题就变成了如何降低时间复杂度的问题,我们可以不用暴力法,
既然我一开始是一个一个查找并且就是这里在数据大时耗时  所以想到改用二分法查找
既然涉及到二分查找那就讲一下原理: 


二分法查找原理: 1.它和排序是绑定在一起的(升序) 
                      2.如找到等于10的数 :

              
                    由以上可只二份查找有两个关键点:
                一是:查找的数组一定是排序的
             二是: 它是通过移动左右两个边界进行缩小查找范围。
         (在很大的数据时一个一个遍历是很耗时的 ) 
           三是:通过左右之间的中间数进行比较。 
                              


具体思路:
方法  1:

(思想来源:本文链接:7-8 三足鼎立 (25分)__努力努力再努力_的博客-CSDN博客) 
(1)找规律(画图解析):

1.先将数组整个排序,按照从大到小的顺序,然后遍历,
                  2. 将这个数固定为选的一条边,结合自已输入的(本国的数)这样就有同时的两条边,
                   现在只需要确定第三条边就行
                   
    3.你可能会问16不也满足吗?是的,但在遍历到16时就已经把27的情况考虑啦
    所以每次都是从i+1到n中选。               
        4.可以看出围绕r-l+1来做 ; 
        5.现在我们需要做的就是二分法找到大于最小边界的数和小于最大边界的数即可,
        6.然后下标相减就是所要的(这只是一次的结果) */


#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int cmp(const void *a,const void *b){
	return *(int *)a-*(int *)b;
}
int main() 
{
	long long n,p,y,i,h,sum=0;
	scanf("%lld %lld",&n,&p);
	y=n+1;//方便从1开始标记 
	long long a[y];
	a[0]=0;
	for(i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	//排序(升序) 
	qsort(a,y,sizeof(long long),cmp);
	//开始用二分法
	long long left,right;
	long long m;//存中间位的下标
	long long num1,num2;//存每次大于最小值的边界下标和每次小于最大值的边界下标 
	for(i=1;i<=n;i++){
		left=i+1;
		right=n;
		long long max,min;//每次要满足的a[x]<max(两边之和),a[x]>min(两边这差)
		 max=a[i]+p;
		 min=fabs(a[i]-p);
		 //接下来确定大于最小值的边界
		 while(left<=right){//这个一般都是作位二分查找的循环条件 
		 	m=(right+left)/2;//每次它都会变一定要放在里面,注意中间下标是加号不是减号!!!
			//找左位关键是a[m]小于min即min在a[m]和right之间才会移动左位 
			//在这里右位不是关键 
			 if(a[m]<=min){
			 	left=m+1;
			 } else{
			 	right=m-1;
			 }
		 } 
		 num1=left;
		 left=i+1;
		 right=n; 
		 //找小于最大值的边界的小标
		 while(left<=right){
		 	m=(right+left)/2;
		 	if(a[m]>=max){
		 		right=m-1;
			 } else{
			 	left=m+1;
			 }
		 }
		 num2=right;
		h=(num2-num1)+1;//h只是一次循环的情况
		sum=sum+h;//sum是总和
	} 
	printf("%lld",sum);
	return 0;
}
		

  • 14
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小丞啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值