FZU ACM寒假第二讲

 1.二分查找

此题是对二分法的模版的考察,二分法是一种快速寻找数组中是否存在特定数字的快速方法,采用每次去中间值,改变左右两边界的操作方法在数据量大时可以有效减少程序的时间复杂度,针对本题(此题数组已按左小右大排列),我们首先输入数组,并确立左边界L初始为1,右边界R为n,再取中间值mid=(L+R)/2,判断第mid个数是否为我们所要找的数,如果第mid数大于我们需要找的数则说明在mid右侧不存在数使得与所求k相等,此时可将右边界改为mid-1;同理,a[mid]<k时,左边界改为mid+1;不断重复操作,知道出现a[mid]==k,即存在,若完整遍历过后(即L>R)则不存在,代码如下

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,a[100005],q,k;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	cin>>q;
	for(int i=1;i<=q;i++)
	{
		cin>>k;
		int l=1,r=n,sum=0;
		while(l<=r)
		{
			int mid=(l+r)/2;
			if(a[mid]==k)
			{
				sum++;
				break;
			}
			else if(a[mid]<k)
			{
				l=mid+1;
			}
			else if(a[mid]>k)
			{
				r=mid-1;
			}
		}
		if(sum==0)cout<<"No"<<endl;
		if(sum!=0)cout<<"Yes"<<endl;
	}
}

2. A-B 数对

给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A−B=C的数对的个数(不同位置的数字一样的数对算不同的数对)。

此题给我们一个正整数数列,我们自然想到二分法,那具体要如何操作呢,首先我们发现要使用二分法数列要有特定规律,所以我们可以采用sort函数使数列从小到大排列,我们再看题目的后半段求A-B=C,可以换一种理解方式,即找数列中是否存在A使得B+C=A;此时我们可以遍历整个数组,取第i个数时,对i+1到n的数进行二分法查找存在几个相同数使a[i]+c=a[j](j>i);判断是否存在容易,但如何判断存在几个呢,这里介绍两个函数

lower_bound(a+1,a+n+1,x)-a //下标从1开始

 上述函数可以求出第一个x位置,如果x不存在则求出大于x的最小数的位置

upper_bound(a+1,a+n+1,x)-a //下标从1开始

 上述函数可以求出第一个大于x的位置

很明显二者相减即是x的个数,到此此题已经解答,代码如下

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long n,c,a[200005];
	long long ans=0;
	cin>>n>>c;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		ans+=((upper_bound(a+1,a+n+1,a[i]+c)-a)-(lower_bound(a+1,a+n+1,a[i]+c)-a));
	}
	cout<<ans;
}

3.分巧克力

巧克力规则:形状是正方形,边长是整数,大小相同。

首先我们通过数学分析可以发现一块a*b大小的巧克力边长为x时可以分成(a/x)*(b/x)块巧克力,因为此题要我们求最终最大的边长,所以我们可以采用二分法,此题最后的难点就是check的书写(下方代码函数f即为check函数),由上方我们可以发现第i块巧克力可以分成的份数的规律,所以我们只需遍历每一个巧克力可分成的份数,再相加得s,只要s大于总人数即符合条件,代码如下

#include<bits/stdc++.h>
using namespace std;
int n,a[100003],b[100003],k;
bool f(int x)
{
	int s=0;
	for(int i=1;i<=n;i++)
	{
		s+=(a[i]/x)*(b[i]/x);
	}
	if(s>=k)return true;
	else return false;
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]>>b[i];
	}
	int l=1,r=10000;
	while(l<r)
	{
		int mid=(r+l+1)/2;
		if(f(mid))l=mid;
		else r=mid-1;
	}
	cout<<l;
}

4.卡牌

有 n张卡牌,其中每种卡牌各一张,那么这 n 张卡牌可以被称为一套牌。小明为了凑出尽可能多套牌,拿出了 m 张空白牌, 他可以在上面写上数 i,将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观,决定第 i 种牌最多手写 bi张。问小明最多能凑出多少套牌?

认真研读题目我们发现如果想要直接获得精确的答案非常复杂,但先假设答案再带入验证是否成立却简易很多,那么此题完全可以使用二分法来快速缩小我们答案的范围

那就讲讲最核心的check函数(下方代码为f函数),我们将假设的答案进入check中,遍历a数组(即小明手中各种牌的数量),只要满足a[i]+b[i]<=x即这个答案不成立,二分的右边界变为R=mid

重复操作最终可得答案,代码如下

#include<bits/stdc++.h>
using namespace std;
long long n,a[200003],b[200003],m,l=0,r=1e7;
bool f(int x)
{
	long long sum=0;
	for(int i=1;i<=n;i++)
	{
		if(x-a[i]>b[i])return false;
		sum+=max(x-a[i],0ll);
	}
	return (sum<=m)?true:false;
}
long long solve(){
	while(l+1<r){
		long long mid=(l+r)/2;
		if(f(mid)){
			l=mid;
		}
		else r=mid;
	}
	if(f(r)) return r;
	return l;
}
int main()
{
	cin>>n>>m;
	for(long long i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	for(long long i=1;i<=n;i++)
	{
		cin>>b[i];
	}
	cout<<solve();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值