团队训练(六)

团队训练(六)-- 二分查找(2)

前言:这次的题目从易到难,前四道题无脑题,后三题就有一些对我来说的新东西,不过整体的难度还是适中吧,感觉重点成了数学,数学真的太可怕了,真是让人头大。还有就是,开学了,更能有高效率肝题了,奥利给!

1.奇怪的函数
题目

使得 x^x
达到或超过 n 位数字的最小正整数 x 是多少?

输入格式
一个正整数 n

输出格式
使得 x^x
达到 n 位数字的最小正整数 x

输入输出样例

输入
11

输出
10

说明/提示
n<=2000000000

解析:利用cmath函数库中的log函数。
因为n的范围已经达到e9的数量级(这里是),用long long都出不来x的值,因为是e9的位数的数字,但是可以用达到n位数字的最小整数=10(n-1),但是这里是要求xx,所以就log(x^x) = x * log(x)即可,接着就是二分找x。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;

int main()
{
	LL n, cnt;
	cin >> n;
	LL l = 0, r = 2e9;
	while(l < r)
	{
		LL mid = l + r >> 1;
		cnt = (mid * log10(mid * 1.0)) + 1;
		if(cnt < n)
			l = mid + 1;
		else
			r = mid;
	}
	cout << l;
	return 0;
}

2.烦恼的高考志愿
题目

计算机竞赛小组的神牛V神终于结束了万恶的高考,然而作为班长的他还不能闲下来,班主任老t给了他一个艰巨的任务:帮同学找出最合理的大学填报方案。可是v神太忙了,身后还有一群小姑娘等着和他约会,于是他想到了同为计算机竞赛小组的你,请你帮他完成这个艰巨的任务。

题目描述
现有 m(m≤100000) 所学校,每所学校预计分数线是 ai(ai≤10^6)。有 n(n≤100000) 位学生,估分分别为bi(bi≤10^6)。根据n位学生的估分情况,分别给每位学生推荐一所学校,要求学校的预计分数线和学生的估分相差最小(可高可低,毕竟是估分嘛),这个最小值为不满意度。求所有学生不满意度和的最小值。

输入格式
第一行读入两个整数m,n。m表示学校数,n表示学生数。第二行共有m个数,表示m个学校的预计录取分数。第三行有n个数,表示n个学生的估分成绩。

输出格式
一行,为最小的不满度之和。

输入输出样例
输入
4 3
513 598 567 689
500 600 550

输出
32

说明/提示
数据范围:

对于30%的数据,m,n<=1000,估分和录取线<=10000;

对于100%的数据,n,m<=100,000,录取线<=1000000。

解析:将学校的分数线进行排序,然后在让每个学生去找它分数的左边界和右边界,然后做个差值进行比较一下即可。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;

int m, n, grade = 0;
int a[maxn], b[maxn];

int binary_search1(int k)
{
	int l = 1, r = m;
	while(l < r)
	{
		int mid = l + r + 1 >> 1;
		if(a[mid] > k)
			r = mid - 1;
		else
			l = mid;
	}
	return l;
}

int binary_search2(int k)
{
	int l = 1, r = m;
	while(l < r)
	{
		int mid = l + r >> 1;
		if(a[mid] < k)
			l = mid + 1;
		else
			r = mid;
	}
	return l;
}

int main()
{
	cin >> m >> n;
	for(int i = 1; i <= m; i++)
		cin >> a[i];
	for(int i = 1; i <= n; i++)
		cin >> b[i];
	sort(a + 1, a + m + 1);
	for(int i = 1; i <= n; i++)
	{
		int tempPos1 = binary_search1(b[i]);
		int tempPos2 = binary_search2(b[i]);	
		if(abs(b[i] - a[tempPos1]) > abs(b[i] - a[tempPos2]))
			grade += abs(b[i] - a[tempPos2]);
		else
			grade += abs(b[i] - a[tempPos1]);
	}
	cout << grade;
	return 0;
}

3.完全平方数
题目
多次查询[l,r]范围内的完全平方数个数

定义整数x为完全平方数当且仅当可以找到整数y使得y*y=x

输入描述:
第一行一个数n表示查询次数
之后n行每行两个数l,r
输出描述:
对于每个查询,输出一个数表示答案

输入
5
1 3
1 4
2 4
4 4
1 1000000000

输出
1
2
1
1
31622

备注:
n <= 100000
0<= l <= r <= 1000000000

解法一:对左边界进行开方,右边界也开方,然后再判断一下左边界是否是完全平方数,不是就向上取整即可。

解法二:二分查找mid * mid最靠近l右边的数,和mid*mid最靠近r左边的数,然后注意一下0也是完全平方数就行。

解法一代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int t;

int main()
{
	cin >> t;
	while(t --)
	{
		int l, r;
		cin >> l >> r;
		int tempR = sqrt(r);
		int tempL = sqrt(l);
		if(tempL * tempL != l)
			tempL++;
		cout << tempR - tempL + 1 << endl;
	}
	return 0;
}

解法二代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int t;

LL binary_search1(LL l, LL r)
{
	LL temp_l = l, temp_r = r;
	l = 0;
	while(l < r)
	{
		LL mid = l + r >> 1;
		if(mid * mid < temp_l)
			l = mid + 1;
		else
			r = mid;
	}
	return l;
}

LL binary_search2(LL l, LL r)
{
	LL temp_l = l, temp_r = r;
	l = 0; //特判0 
	while(l < r)
	{
		LL mid = l + r + 1 >> 1;
		if(mid * mid > temp_r)
			r = mid - 1;
		else
			l = mid;
	}
	return r;
}

int main()
{
	cin >> t;
	while(t --)
	{
		LL l, r;
		cin >> l >> r;
		LL tempL = binary_search1(l ,r);
		LL tempR = binary_search2(l ,r);
			cout << tempR - tempL + 1 << endl;
	}	
	return 0;
}

4.解方程
题目

对于方程 2018 * x ^ 4 + 21 * x + 5 * x ^ 3 + 5 * x ^ 2 + 14 = Y,
告诉你Y的值,你能找出方程在0~100之间的解吗?
输入描述:
第一行输入一个正整数T(表示样例个数)
接下来T组样例
每组样例一行,输入一个实数Y
输出描述:
一行输出一个样例对应的结果,
输出方程在0~100之间的解,保留小数点后4位小数;如果不存在,输出 -1

输入
2
1
20180421

输出
-1
9.9993

解析:浮点型二分,在二分查找(1)那细讲了,这里就多了一个解的区间,加一个判断就行。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e6 + 7;
const double eps = 1e-6;
const long long mod = 1e9 + 7;
const long long inf = 0x3f3f3f3f;
int t;
double y;

int judge(double x) 
{
	double temp = 2018 * x * x * x * x + 21 * x + 5 * x * x * x + 5 * x * x + 14;
	if(temp <= y)
		return 1;
	else
		return 0;
	
}

int main()
{
	cin >> t;
	while(t --)
	{
		cin >> y;
		double l = -mod, r = mod;
		while(r - l > eps)
		{
			double mid = (l + r) / 2;
			if(judge(mid))
				l = mid;
			else
				r = mid;
		}
		if(l >= 0 && l <= 100)
			printf("%.4lf\n", l);
		else
			printf("-1\n"); 
	}
	return 0;
}

5.接机
题目

一场别开生面的牛吃草大会就要在Farmer John的农场举办了!
世界各地的奶牛将会到达当地的机场,前来参会并且吃草。具体地说,有N头奶牛到达了机场(1≤N≤105),其中奶牛i在时间ti(0≤ti≤109)到达。Farmer John安排了M(1≤M≤10^5)辆大巴来机场接这些奶牛。每辆大巴可以乘坐C头奶牛(1≤C≤N)。Farmer John正在机场等待奶牛们到来,并且准备安排到达的奶牛们乘坐大巴。当最后一头乘坐某辆大巴的奶牛到达的时候,这辆大巴就可以发车了。Farmer John想要做一个优秀的主办者,所以并不想让奶牛们在机场等待过长的时间。如果Farmer John合理地协调这些大巴,等待时间最长的奶牛等待的时间的最小值是多少?一头奶牛的等待时间等于她的到达时间与她乘坐的大巴的发车时间之差。

输入保证MC≥N。

输入描述:
输入的第一行包含三个空格分隔的整数N,M和C。第二行包含N个空格分隔的整数,表示每头奶牛到达的时间。
输出描述:
输出一行,包含所有到达的奶牛中的最大等待时间的最小值。

输入
6 3 2
1 1 10 14 4 3

输出
4
说明
如果两头时间1到达的奶牛乘坐一辆巴士,时间2和时间4到达的奶牛乘坐乘坐第二辆,时间10和时间14到达的奶牛乘坐第三辆,那么等待时间最长的奶牛等待了4个单位时间(时间10到达的奶牛从时间10等到了时间14)。

解析:这道题居然是今天下午临场写出来的,不过前期也已经有一点思路了,主要还是写check函数卡住了,这题是二分最大的等待时间,在check中尽可能的让时间较接近的牛坐一起,有点贪心的思想???然后假如一辆车坐满了就坐下一量,或者这头牛到达时间和它那辆车第一到达的那头牛时间相差大于二分最大的等待时间也要坐下一辆车。最后就是需要的车数与拥有的车数进行比较,假如需要的车数大于拥有的车数,说明最大等待时间太短,必须扩大最大等待时间,反之你懂的。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const long long mod = 1e9 + 7;
const long long inf = 0x3f3f3f3f;
LL t[maxn];
int n, m, c;

int check(int T)
{
	int num = 1, tempC = c - 1, bg = t[1];
	for(int i = 2; i <= n; i++)
	{
		if(t[i] - bg > T || tempC == 0)
		{
			bg = t[i];
			num ++;
			tempC = c - 1;
		}
		else
			tempC--;
	}
	if(num > m)
		return 1;
	else
		return 0;
}

int main()
{
	cin >> n >> m >> c;
	for(int i = 1; i <= n; i++)
		cin >> t[i];
	sort(t + 1, t + n + 1);
	LL l = 0, r = mod;
	while(l < r)
	{
		int mid = l + r  >> 1;
		if(check(mid))
			l = mid + 1;
		else
			r = mid;
	}
	cout << l;
	return 0;
} 

6.wyh的物品
题目

wyh学长现在手里有n个物品,这n个物品的重量和价值都告诉你,然后现在让你从中选取k个,问你在所有可能选取的方案中,最大的单位价值为多少(单位价值为选取的k个物品的总价值和总重量的比值)

输入描述:
输入第一行一个整数T(1<=T<=10)
接下来有T组测试数据,对于每组测试数据,第一行输入两个数n和k(1<=k<=n<=100000)
接下来有n行,每行两个是a和b,代表这个物品的重量和价值
输出描述:
对于每组测试数据,输出对应答案,结果保留两位小数

输入
1
3 2
2 2
5 3
2 1

输出
0.75
说明
对于样例来说,我们选择第一个物品和第三个物品,达到最优目的

解析:01分数规划 + 二分。
刚开始的思路是贪心,以为直接找出比值最大的那几个物品重量,然后价值求和除重量求和,结果样例是0.71,那贪心就翻车。然后题意就是sigma v / sigma w >= x, -> sigma v - x * sigma w >= 0, -> v1 - x * w1 + v2 - x * w2 + ... + x * wk >= 0 (排序后,找最大的那几个,最大那几个都小于0,其他肯定小于0)一开始被数学搞傻了,然后就判断一下,是不是二分所求的比值过小,那么这个求和会 >= 0,那左端点右移,反之右移。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const double eps = 1e-6;
const long long mod = 1e9 + 7;
const long long inf = 0x3f3f3f3f;
int t, n, k;

struct project
{
	double w, v;
}p[maxn];

bool cmp(double b, double c)
{
	return b > c;
}

int judge(double x)
{
	double a[maxn], sum = 0;
	for(int i = 1; i <= n; i++)
		a[i] = p[i].v - x * p[i].w;
	sort(a + 1, a + n + 1, cmp);
	for(int i = 1; i <= k; i++)
		sum += a[i];
	return sum >= 0;	
}

int main()
{
	cin >> t;
	while(t --)
	{
		cin >> n >> k;
		memset(p, sizeof(p),0);
		double cnt = 0;
		for(int i = 1; i <= n; i++)
			cin >> p[i].w >> p[i].v;
		double l = 0, r = 1e6;
		while(r - l > eps)
		{
			double mid = (l + r) / 2;
			if(judge(mid))
				l = mid;
			else
				r = mid;
		}
		printf("%.2lf\n", l);
	}
	return 0;
}


7.借教室
题目

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj, sj, tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

输入描述:
第一行包含两个正整数n, m,表示天数和订单的数量。

第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。

接下来有m行,每行包含三个正整数dj, sj, tj,表示租借的数量,租借开始、结束分别在第几天。

每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。

输出描述:
如果所有订单均可满足,则输出只有一行,包含一个整数0。否则(订单无法完全满足)输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

输入
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4

输出
-1
2
说明
第1 份订单满足后,4 天剩余的教室数分别为0,3,2,3。
第2 份订单要求第2 天到第4 天每天提供3 个教室,而第3 天剩余的教室数为2,因此无法满足。分配停止,通知第2个申请人修改订单。

备注:
对于10%的数据,有1≤n,m≤10;
对于30%的数据,有1≤n,m≤1000;
对于70%的数据,有1≤n,m≤105;
对于100%的数据,有1≤n, m≤106, 0≤ri, dj≤109, 1≤sj≤tj≤ n。

解析:差分 + 二分
u1s1,第一次接触差分,还不是很熟练,这里实际就是开了一个差分数组,原数组的含义是前mid个订单中,每天所需的教室数目,然后用差分数组在开始的区间加上需要租用的教室,在结尾的区间再下一天减去需要租用的教室,最后来个前缀和算出每天所需教室的总数是不是足够即可,时间复杂度为O(log(m)* (m + n))。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 1e6 + 5;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int r[maxm], delt[maxm];
int n, m;
struct order
{
	int d, s, e;
}o[maxm]; 

int check(int k)
{
	memset(delt, 0, sizeof(delt));
	for(int i = 1; i <= k; i++)
	{
		delt[o[i].s] += o[i].d;
		delt[o[i].e + 1] -= o[i].d;
	}
	for(int i = 1; i <= n; i++)
	{
		delt[i] += delt[i-1];
		if(delt[i] > r[i])
			return 0;
	}
	return 1;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
		scanf("%d", &r[i]);
	for(int i = 1; i <= m; i++)
		scanf("%d%d%d", &o[i].d, &o[i].s, &o[i].e);
	int l = 1, r = m;
	while(l < r)
	{
		int mid = l + r >> 1;
		if(check(mid))
			l = mid + 1;
		else
			r = mid;
	}
	if(l == m)
		printf("0\n");
	else
		printf("-1\n%d", l);
}

posted @ 2020-05-31 23:02  ~K2MnO4  阅读(543)  评论(0)    收藏  举报