“华为杯”杭州电子科技大学2023新生编程大赛_大雪球_二分

题目链接

任意两个雪球合成一个大雪球,大雪球的体积等于两个小雪球的体积之和,找体积第 k 小的大雪球的体积。

二分,分的是合成后大雪球的体积

找合成后的雪球体积小于中间值的组合数,大于等于k说明 体积第 k 小的大雪球的体积  在左区间,小于k说明  体积第 k 小的大雪球的体积  在右区间

在未知数组中找第k小的数

代码:

#include<iostream>
#include<algorithm>
using namespace std;

int n,k;
const long long int N=1e6;
long long int *a = new long long int[N];

bool check(long long int m);
int main()
{
	int t,i;
	cin>>t;
	while(t--){
        //数据输入
		cin>>n;
		for(i = 0; i<n; i++)cin>>a[i];
		cin>>k;

		sort(a,a+n);   //升序排列
		
		long long int l = a[0]+a[1];    //左边界
		long long int r = a[n-2]+a[n-1];//右边界
		
		while(l<r){
			long long int m = (l+r)>>1; //中间值
			if(check(m)){
				r = m;        //合成后的雪球体积小于中间值的组合数大于等于k,选左
			}else{
				l = m+1;      //合成后的雪球体积小于中间值的组合数小于k,选右
			}
		}
		cout<<l-1<<endl;
		
	}
	
}
//找合成后的雪球体积小于中间值的组合数,大于等于k返回true,否则返回false
bool check(long long int m)
{
	long long int cnt = 0;
	int i,j;
	int t = n-1;
    //双指针遍历
	for(i = 0; i<n; i++){
		for(j = t; j>i; j--){
			if(m > a[i]+a[j]){
				cnt += j-i;        //j从大到小,a[j]也从大到小,即数组是升序的,下标减小数组值也减小
                                   //当a[j]满足条件时,a[j-1],a[j-2]……也满足条件,j-i是满足条件的组合数
				break;
			}
		}
		t = j;    //每循环一次i增加,eg:若m<a[1]+a[8],则m<a[2]+a[8]一定成立
                  //所以下一轮t从j(当前轮次满足满足m > a[i]+a[j])开始即可
	}
	
	if(cnt>=k)return true;
	else return false;
}

失败的尝试:

想通过标记出等于中间值的,减少二分次数

但  中间值不一定在目标数组中       

eg: 假设目标数组为     2 3 5 6   (2+6)/2 = 4 不在目标数组中

#include<iostream>
#include<algorithm>
using namespace std;

int n,k;
const long long int N=1e6;
long long int *a = new long long int[N];

int check(long long int m);
int main()
{
	int t,i;
	cin>>t;
	long long int m;
	while(t--){
		cin>>n;
		for(i = 0; i<n; i++)cin>>a[i];
		cin>>k;
		sort(a,a+n);
		
		long long int l = a[0]+a[1];
		long long int r = a[n-2]+a[n-1];
		
		while(l<r){
			m = (l+r)>>1;
			if(check(m) == -1){
				r = m;
			}else if(check(m) == 1){
				l = m+1;
			}else break;
		}
		if(check(m) != 0)cout<<l-1;
		else cout<<m;
		
	}
	
}
int check(long long int m)
{
	long long int cnt = 0;
	int i,j;
	int t = n-1;
	for(i = 0; i<n; i++){
		for(j = t; j>i; j--){
			if(m > a[i]+a[j]){
				cnt += j-i;
				break;
			}
		}
		t = j;
	}
	
	if(cnt>k)return -1;
	else if(cnt<k)return 1;
	else return 0;
}

参考:http://t.csdnimg.cn/dwz9q

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值