二分专题【二分查找】

二分专题

今天训练的是二分查找专题,刷完专题后对二分法的应用有了新的理解。

在平时写题中,有些时候会遇到题目要求给出诸如给定划分方式范围内的最大值或是最短距离的最大值等,此类问题枚举虽然可以理论上解决,但是由于题目一般数据量较大,暴力枚举没有什么AC机会。在这里便用到了时间复杂度为logN的更高效率的方法:二分查找。

在有序表中查找元素时常常使用二分查找法,因此在遇到此类题目时若题目条件允许变动给出数组的元素位置,对元素进行排序会有益于解题。二分法基于逐步缩小范围的思维方法,每次二分都会使搜索范围缩小一半,在有序的元素中快速接近要查找的元素。 


目录

二分专题

A - Aggresive Cows

           二分查找的方法

B - Drying

C - Count on Canton

D - River Hopscotch

E - Monthly Expence

F - Pie

G - Expanding Rods


 

A - Aggresive Cows

Time limit

1000 ms

Memory limit

65536 kB

Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,...,xN (0 <= xi <= 1,000,000,000).

His C (2 <= C <= N) cows don't like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?

Input

* Line 1: Two space-separated integers: N and C 

* Lines 2..N+1: Line i+1 contains an integer stall location, xi

Output

* Line 1: One integer: the largest minimum distance

Sample Input

5 3
1
2
8
4
9

Sample Output

3

Hint

OUTPUT DETAILS: 

FJ can put his 3 cows in the stalls at positions 1, 4 and 8, resulting in a minimum distance of 3. 

Huge input data,scanf is recommended.

题意

农夫约翰有一群C只不安分的牛,他们对约翰的牛栏有很大意见,导致他们被放入牛栏后就会开始斗牛,为了不出牛命,约翰想要求出给定的N个直线排列的牛栏中,每只牛被放入后两牛之间最小距离的最大值是多少。

思路

最小距离的最大值,emmmmmm,你说的很好,那就二分查找吧。首先将所有牛栏的位置读入后按位置排序,得到适用于二分查找的升序表。

二分查找的方法

那么现在问题来了눈_눈,说是二分法,对啥玩意儿进行二分啊?此处我们要查找的量是距离,那么应该用可以取到的距离的最大值和最小值进行二分。最大距离就是整个牛栏的长度,也就是第一个牛栏到最后一个牛栏的距离,最小距离,可求出每个牛栏之间能取到的最小距离,但是敲代码好麻烦.........就取0吧。于是我们得到了二分的两个端点,small和large(其实更形象的应该用left和right,左右端点嘛),接下来我们可以得出两端点的中点mid=(small+large)/2,这个mid便是我们本次要尝试将牛放进去的最小间隔距离。怎么放呢?第一个牛栏不管怎么说肯定是得放的对吧,第二个牛栏要不要放呢?看看第二个牛栏的位置和第一个牛栏差值是不是大于等于mid,发现是小于的,放不了。既然第二个牛栏放不了,我们就要比较第一个和第三个牛栏间的距离够不够放,这次够了,我们往第三个牛栏里塞一头吧。接下来比较的就不是第一个牛栏和第四个牛栏间的距离了,而是刚刚放入牛的第三个和第四个间的距离,原因你懂的。因此我们需要一个变量来存储最近一次放入牛的牛栏位置。

这样我们每碰到一个符合要求的牛栏就塞一头牛,一轮遍历完所有牛栏后,得到了在最小距离为mid时能放进牛栏的牛总数。但是我们可能发现能放的数目大于我们拥有的牛数目,说明其实我们可以让间隔更大点的,这个最小距离mid开小了。于是让small=mid+1,将下一轮搜索的区间定在刚刚搜索完区间的右半段,也就是数值较大的半段。亦或是我们得到了能放的数目小于我们拥有的牛数,说明范围开太大了,于是large=mid-1,搜索刚刚的左半段。还有一张情况就是我们得到了刚好等于目前拥有牛数的可放入数,但是我们不能返回,我们要贪心一点,得试试要是距离再开大一点点,是不是还是得到这个数,于是依然small=mid+1,往大的半段搜索。当我们一路搜索到large

上面的两段恰好可以切分为两个函数,上面一段的主要操作是在每个给定最小距离mid中判断放下了多少牛,下面一段的主要操作是判断上面一段给出的可放牛数是否符合要求并依此求出新的mid继续让上一段尝试放牛。于是函数功能,传递的参数和返回值我们都清楚了,是时候写函数了。

//这里是第一段,判断在给定最小距离的情况下能塞下多少头牛
int putcows(int dis)
{
    //p储存最近一次放入牛的牛栏位置,我们在初始化时就在第一个牛栏中放牛了,因此计数变量=1
	int counts = 1, p = stall[1];
	for(int i=2;i<=n;i++)
	{
        //如果当前牛栏和上次放牛的牛栏间距大于最小距离,放牛计数器+1,最近放牛的牛栏为当前牛栏
		if (stall[i] - p >= dis)
		{
			counts++;
			p = stall[i];
		}
	}
    //把统计好的可放牛数目传回去
	return counts;
}

//这里是第二段,负责最初给出mid值,并通过传回的可放牛数调整接下来给出的mid值
int find()
{
    //初始化最初的端点
	int small = 0, large = stall[n] - stall[1], mid;
	while(small<=large)
	{
		//最小值取为中间
		mid = (small + large) / 2;
		//通过函数返回在当前最小值情况下能够放进的牛数目
		int puts = putcows(mid);
		//此处不能多写(puts==c)return mid;遇到数目等于应当贪心一点把最小值扩大试试看
		if (puts >= c)small = mid + 1;
		else if (puts < c)large = mid - 1;
	}
    //把未被贪心步骤改变的那个值传回主函数
	return large;
}

至此二分查找的写法就出来了,我们再写个主函数,添加点全局变量啥的就可以愉快的去 WA AC了。

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define N 100005

using namespace std;

int stall[N];
int n, c;

int putcows(int dis)
{
	int counts = 1, p = stall[1];
	for(int i=2;i<=n;i++)
	{
		if (stall[i] - p >= dis)
		{
			counts++;
			p = stall[i];
		}
	}
	return counts;
}

int find()
{
	int small = 0, large = stall[n] - stall[1], mid;
	while(small<=large)
	{
		//最小值取为中间
		mid = (small + large) / 2;
		//通过函数返回在改最小值情况下能够放进的牛数目
		int puts = putcows(mid);
		//此处不能多写(puts==c)return mid;遇到数目等于应当继续往下判
		if (puts >= c)small = mid + 1;
		else if (puts < c)large = mid - 1;
	}
	return large;//return small-1;
}

int main()
{
	while(scanf("%d%d",&n,&c)!=EOF)
	{
		int i;
		for(i=1;i<=n;i++)
		{
			scanf("%d", &stall[i]);
		}
        //对胡乱输入的牛栏位置排序
		sort(stall + 1, stall + n + 1);
        //find()函数为查找函数
		printf("%d\n", find());
	}
}

 


B - Drying

Time limit

2000 ms

Memory limit

65536 kB

It is very hard to wash and especially to dry clothes in winter. But Jane is a very smart girl. She is not afraid of this boring process. Jane has decided to use a radiator to make drying faster. But the radiator is small, so it can hold only one thing at a time.

Jane wants to perform drying in the minimal possible time. She asked you to write a program that will calculate the minimal time for a given set of clothes.

There are n clothes Jane has just washed. Each of them took ai water during washing. Every minute the amount of water contained in each thing decreases by one (of course, only if the thing is not completely dry yet). When amount of water contained becomes zero the cloth becomes dry and is ready to be packed.

Every minute Jane can select one thing to dry on the radiator. The radiator is very hot, so the amount of water in this thing decreases by k this minute (but not less than zero — if the thing contains less than k water, the resulting amount of water will be zero).

The task is to minimize the total time of drying by means of using the radiator effectively. The drying process ends when all the clothes are dry.

Input

The first line contains a single integer n (1 ≤ n ≤ 100 000). The second line contains ai separated by spaces (1 ≤ ai ≤ 109). The third line contains k (1 ≤ k≤ 109).

Output

Output a single integer — the minimal possible number of minutes required to dry all clothes.

Sample Input

sample input #1
3
2 3 9
5

sample input #2
3
2 3 6
5

Sample Output

sample output #1
3

sample output #2
2

题意

(别管那些#sample input什么的)Jane洗衣服,想要不择手段地在最快的时间内把所有衣服弄干。她有两种手段,一种是晾在那自然风干,另一种是放在暖气片上。在自然风干的时候,每分钟衣服含水量蒸发1,在暖气片上烘干时,每分钟衣服含水量蒸发k,但是暖气片一次只能放一件衣服。一件衣服的暖气烘干和其他衣服的风干是同时进行的。给出衣服件数,每件衣服开始晾晒时的含水量,暖气片每分钟能蒸发的水量,问最少多久能将所有衣服晾干(所有衣服含水量为0)。

思路

一开始我天真的以为这是个贪心问题,每分钟用暖气片烘含水最多的那件,但是看到数据量我就懵圈了,这是要TLE的节奏。冥思苦想不得如何二分后,经过题解dalao们的启发,成功的打开了新世界的大门

此题求解的是时间,那么我们就对时间进行二分,取最大理论时间,也就是水最多的那件自然风干的时间,以及最小理论时间1分钟(若有一件可以由暖气片一分钟烘干的衣服和其他一分钟可以晾干的衣服)为初始区间的左右端点,取中点时间。

在取到中点时间mid后,我们分析一下目前的情况,在经过这么长的时间后,所有的衣服在晾晒过程中都蒸发了mid*1的水分,有些衣服干了(cloth[i]-mid*1<=0),但可能会有些大袄子没干(cloth[i]-mid*1>0),这种时候我们就通过时间穿梭在所有衣服仍在晾晒时对最后晾不干的衣服使用烘干大法,每分钟的暖气片烘干会使这件衣服最终含水量减少k-1(因为最后的含水量包括了这一分钟本来风干蒸发的水量,因此理论值只减去k-1),我们持续对这件衣服使用烘干大法,直到其最终含水量为0(小于等于0),记录使用暖气片的次数,每次烘干耗时一分钟,对所有最终没干的衣服都使用一定次数的烘干大法后,得到的使用暖气片总次数即为使用暖气片的时间count,如果目前的理论时间mid成立,那么暖气片的使用时间也应在这个时间范围内,而且最有效率的情况就是每时每刻暖气片都在使用。我们比较mid和count的大小,若count>mid,说明理论时间太短,不成立,应往右区间继续二分,若count

AC代码

//#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
//#define INF 0x3f3f3f3f

using namespace std;

long long int cloth[100005];
long long int n, k;

//统计使用暖气片时间的函数
long long int dryer(long long int dryt)
{
	long long int sum = 0;
	for(int i=1;i<=n;i++)
	{
		if (temp > dryt)
		{
            //ceil函数求大于等于表达式的最小整数,因此若除出小数必进1
			sum += ceil((cloth[i] - dryt)*1.0 / (k - 1));//计算要使用几分钟暖气片才能烘干
		}
	}
    //返回使用暖气片的总时间
	return sum;
}

//初始化二分区间,给出mid并通过返回的时间调整mid值的函数
long long int dry()
{
    //初始化端点
	long long int low = 1, high = cloth[n];
	long long int res;
	while(low<=high)
	{
		long long int mid = (low + high) / 2;
        //从统计函数得到使用暖气片的时间并同理论最短时间比较,给出新的mid值
		long long int time = dryer(mid);
		if(time<=mid)
		{
			high = mid - 1;
		}
		else if(time>mid)
		{
			low = mid + 1;
		}
	}
    //返回等于时继续搜索中未被改变的端点值
	return low;
}

int main()
{
	int i;
	scanf("%lld", &n);
	for (i = 1; i <= n; i++)scanf("%lld", &cloth[i]);
	scanf("%lld", &k);
	sort(cloth + 1, cloth + n + 1);
	if (k == 1)printf("%d\n", cloth[n]);
    //二分查找在这儿
	else printf("%lld\n", dry());
	return 0;
}

 


C - Count on Canton

Time limit

1000 ms

Memory limit

30000 kB

One of the famous proofs of modern mathematics is Georg Cantor's demonstration that the set of rational numbers is enumerable. The proof works by using an explicit enumeration of rational numbers as shown in the diagram below. 

1/1 1/2 1/3 1/4 1/5 ...
2/1 2/2 2/3 2/4
3/1 3/2 3/3
4/1 4/2
5/1


In the above diagram, the first term is 1/1, the second term is 1/2, the third term is 2/1, the fourth term is 3/1, the fifth term is 2/2, and so on.

Input

The input list contains a single number per line and will be terminated by endof-file.

Output

You are to write a program that will read a list of numbers in the range from 1 to 10^7 and will print for each number the corresponding term in Cantor's enumeration as given below.

Sample Input

3
14
7

Sample Output

TERM 3 IS 2/1
TERM 14 IS 2/4
TERM 7 IS 1/4

题意

要求观察给出的三角形,输出题目给出位置对应的分数。

思路

二分?咕咕咕?.........我是用暴力找规律做的

题目这其实是个三角形,应该斜着看。我们可以发现每行行头的分数分子为行数,分母为1,行尾分数分子为1,分母为行数,中间从左到右分母递增,分子递减,其和总是所在行数+1,每行的分子数均等于行数,嗯规律就是这样。

二分查找大概是在求得分子所在行数后确定位置用的,但是我没有用到。题目中所给出某个数的位置的值(设该数所在行为n)一定小于等于\sum_{i=1}^{n}i也就是其行首数的位置值,因此通过循环累加可以求得最近一个大于等于该位置的行首位置,行首位置与该位置的差值可用于分母和分子的增减,继而求出该位置的分数值,最后还要注意本题位置是蛇形增加的,得加个判断。

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int main()
{
	int query;
	while (scanf("%d", &query) != EOF)
	{
		int  n;
		int res;
		//得到n的行数
		for (n = 1,res=0;res

 


D - River Hopscotch

Time limit

2000 ms

Memory limit

65536 kB

Every year the cows hold an event featuring a peculiar version of hopscotch that involves carefully jumping from rock to rock in a river. The excitement takes place on a long, straight river with a rock at the start and another rock at the end, Lunits away from the start (1 ≤ L ≤ 1,000,000,000). Along the river between the starting and ending rocks, N (0 ≤ N ≤ 50,000) more rocks appear, each at an integral distance Di from the start (0 < Di < L).

To play the game, each cow in turn starts at the starting rock and tries to reach the finish at the ending rock, jumping only from rock to rock. Of course, less agile cows never make it to the final rock, ending up instead in the river.

Farmer John is proud of his cows and watches this event each year. But as time goes by, he tires of watching the timid cows of the other farmers limp across the short distances between rocks placed too closely together. He plans to remove several rocks in order to increase the shortest distance a cow will have to jump to reach the end. He knows he cannot remove the starting and ending rocks, but he calculates that he has enough resources to remove up to rocks (0 ≤ M ≤ N).

FJ wants to know exactly how much he can increase the shortest distance *before* he starts removing the rocks. Help Farmer John determine the greatest possible shortest distance a cow has to jump after removing the optimal set of M rocks.

Input

Line 1: Three space-separated integers: LN, and M 
Lines 2.. N+1: Each line contains a single integer indicating how far some rock is away from the starting rock. No two rocks share the same position.

Output

Line 1: A single integer that is the maximum of the shortest distance a cow has to jump after removing M rocks

Sample Input

25 5 2
2
14
11
21
17

Sample Output

4

Hint

Before removing any rocks, the shortest jump was a jump of 2 from 0 (the start) to 2. After removing the rocks at 2 and 14, the shortest required jump is a jump of 4 (from 17 to 21 or from 21 to 25).

题意

牛在河中间跳石头比赛(什么鬼),FJ想要在移除有限个石头的前提下使石头间距离尽可能大,不能移除起点和终点的石头,输入赛道起终点距离,石头数目和允许移除的石头数目,分别输入石头距离起点的距离,求石头间最短距离的最大值

思路

又是最短距离的最大值,emmmmmmmmm,这很二分。

其实这题和牛栏那题本质上一样(代码上也差不多),都是在求最短距离的最大值,都需要把区间排序便于查找,唯一不一样的一点就是这道题中统计的是在距离内的点数目,牛栏那题是统计的距离外的点数目,具体过程为以0和最远点石头距离为两端点进行二分,过程可参考第一题,在此不多加赘述。

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
//#define INF 0x3f3f3f3f

//枚举最短距离,计算移除的石块数是否大于允许移除的数目
using namespace std;

long long int rock[50005];
int l, n, m;

int counting(int dis)
{
	int p = 0, counter = 0;
	for(int i=1;i<=n;i++)
	{
        //这里两个if的条件和牛栏那道题刚好相反
		if (rock[i] - p < dis&& i != n)
		{
            //此处尝试不加判断i是否为终点石头也AC了,若有dalao明白求解释
			counter++;
		}
		else p = rock[i];
	}
	return counter;
}

int removing()
{
	long long int min = 0, max = rock[n], mid;
	while(min<=max)
	{
		mid = (min + max) / 2;
		int removal = counting(mid);
		if(removal<=m)
		{
			min = mid + 1;
		}
		else if(removal>m)
		{
			max = mid - 1;
		}
	}
	return max;
}

int main()
{
	while(scanf("%d%d%d",&l,&n,&m)!=EOF)
	{
		int i;
		for (i = 1; i <= n; i++)
		{
			scanf("%d", &rock[i]);
		}
		n++;
        //加入提前输入的终点
		rock[n] = l;
		sort(rock + 1, rock + n + 1);
		printf("%d\n", removing());
	}
	return 0;
}

 


E - Monthly Expence

Time limit

2000 ms

Memory limit

65536 kB

Farmer John is an astounding accounting wizard and has realized he might run out of money to run the farm. He has already calculated and recorded the exact amount of money (1 ≤ moneyi ≤ 10,000) that he will need to spend each day over the next N (1 ≤ N ≤ 100,000) days.

FJ wants to create a budget for a sequential set of exactly M (1 ≤ M ≤ N) fiscal periods called "fajomonths". Each of these fajomonths contains a set of 1 or more consecutive days. Every day is contained in exactly one fajomonth.

FJ's goal is to arrange the fajomonths so as to minimize the expenses of the fajomonth with the highest spending and thus determine his monthly spending limit.

Input

Line 1: Two space-separated integers: N and M 
Lines 2.. N+1: Line i+1 contains the number of dollars Farmer John spends on the ith day

Output

Line 1: The smallest possible monthly limit Farmer John can afford to live with.

Sample Input

7 5
100
400
300
100
500
101
400

Sample Output

500

Hint

If Farmer John schedules the months so that the first two days are a month, the third and fourth are a month, and the last three are their own months, he spends at most $500 in any month. Any other method of scheduling gives a larger minimum monthly limit.

题意

FJ想要将他给出的N天划分为M个支出上限相同的财务期间“FJ月”,每个区间存入支出和不超过上限的月份,区间间和区间内的月份均连续,输入N,M,以及N个数字代表每一天的开支,输出在划分区间为M后最小期间支出的最大值

思路

哎呀哎呀又来了,最小啥啥啥的最大值.......

用支出二分,左右端点分别为每天支出的最大值和所有天支出之和,统计能分成几块,统计时逐天相加,若和超过支出限制则新开一个FJ月,计数变量count++,比较返回的月数和要求的月数,若返回月数较大则缩小限制,若较小则扩大限制,相等时往限制较大方向尝试。

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
#define INF 0x3f3f3f3f

//将n天的花费划分为m组,每天必须相邻,求所有分组情况中最高花费的最小值
using namespace std;

int spend[100005];

int n, m, low, high;

int classify(int top)
{
	int sum = 0, counter = 1;
	for(int i=1;i<=n;i++)
	{
		if(sum+spend[i]>top)
		{
			counter++;
			sum = spend[i];
		}
		else sum += spend[i];
	}
	return counter;
}

int arrange()
{
	int mid;
	while(low<=high)
	{
		mid = (high + low) / 2;
		int monthnum = classify(mid);
		if (monthnum > m)
		{
			low = mid + 1;
		}
		else if (monthnum <= m)
		{
			high = mid - 1;
		}
	}
	return low;
}

int main()
{
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		int i;
		high = 0;
		low = 0;
		for(i=1;i<=n;i++)
		{
			scanf("%d", &spend[i]);
			//最大值为所有天花费总和
			high += spend[i];
			//最小值为单月花费最大值
			if (spend[i] > low)low = spend[i];
		}
		printf("%d\n", arrange());
	}
	return 0;
}

 


F - Pie

Time limit

1000 ms

Memory limit

65536 kB

My birthday is coming up and traditionally I'm serving pie. Not just one pie, no, I have a number N of them, of various tastes and of various sizes. F of my friends are coming to my party and each of them gets a piece of pie. This should be one piece of one pie, not several small pieces since that looks messy. This piece can be one whole pie though. 

My friends are very annoying and if one of them gets a bigger piece than the others, they start complaining. Therefore all of them should get equally sized (but not necessarily equally shaped) pieces, even if this leads to some pie getting spoiled (which is better than spoiling the party). Of course, I want a piece of pie for myself too, and that piece should also be of the same size. 

What is the largest possible piece size all of us can get? All the pies are cylindrical in shape and they all have the same height 1, but the radii of the pies can be different.

Input

One line with a positive integer: the number of test cases. Then for each test case:

  • One line with two integers N and F with 1 ≤ N, F ≤ 10 000: the number of pies and the number of friends.
  • One line with N integers ri with 1 ≤ ri ≤ 10 000: the radii of the pies.

Output

For each test case, output one line with the largest possible volume V such that me and my friends can all get a pie piece of size V. The answer should be given as a floating point number with an absolute error of at most 10 −3.

Sample Input

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

Sample Output

25.1327
3.1416
50.2655

题意

在生日会上,要给自己以及F个朋友分派,所有的派高均为1,但半径各不相同,要求所有人分得的派体积一样,每人都只能分到一块派,求能分出的最大值,题目精度要求1e-6。

思路

因为每人都只能分到一份,代表派是不能叠加的,因此只能一刀切下足够分量的派,略过余下不足量的派,在统计函数中应当考虑到这一点。本题还要注意,应处理输出的数为小数,并且有精度要求,因此在二分查找函数中while循环的循环条件为right-left>1e-6,π值通过反余弦函数acos(-1,0)求出,本题精度若用1e7将超时。(以下代码C++AC,G++WA)

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
#define INF 0x3f3f3f3f
double PI = acos(-1.0);

//题意:在每个人只拿到一块大小相等的派的情况下求拿到派体积的最大值(每块派的高度为1,半径为r)
using namespace std;

int n, f;
double v[10005], maxn;

int divide(double volume)
{
    int counter = 0;
    double temp = v[1];
    for(int i=1;i<=n;i++)
    {
        //注意转为int型
        counter += (int)(v[i] / volume);
    }
    return counter;
}

double cut()
{
    double min = 0, mid;
    while (maxn - min > 1e-7)
    {
        mid = (maxn + min) / 2.0;
        int pienum = divide(mid);
        if(pienum>=f)
        {
            min = mid;
        }
        else if(pienum

 


G - Expanding Rods

Time limit

500 ms

Memory limit

32768 kB

When a thin rod of length L is heated n degrees, it expands to a new length L' = (1+n*C)*L, where C is the coefficient of heat expansion.

When a thin rod is mounted on two solid walls and then heated, it expands and takes the shape of a circular segment, the original rod being the chord of the segment.

Your task is to compute the distance by which the center of the rod is displaced. That means you have to calculate h as in the picture.

Input

Input starts with an integer T (≤ 20), denoting the number of test cases.

Each case contains three non-negative real numbers: the initial length of the rod in millimeters L, the temperature change in degrees n and the coefficient of heat expansion of the material C. Input data guarantee that no rod expands by more than one half of its original length. All the numbers will be between 0 and 1000 and there can be at most 5 digits after the decimal point.

Output

For each case, print the case number and the displacement of the center of the rod in single line. Errors less than 10-6 will be ignored.

Sample Input

3

1000 100 0.0001

150 10 0.00006

10 0 0.001

Sample Output

Case 1: 61.3289915

Case 2: 2.2502024857

Case 3: 0

题意

一根长为L的细棒经加热升高n度,其长度伸长为L'=(1+n*C)*L,现在将其两端固定在墙上,热膨胀后细棒会向外弯曲成弧状,求此弧突出的高度,精度取1e-6。

思路

本题通过枚举突出的高度h,从左端点0枚举至右端点R/2,右端点依题意可以确定。通过公式用枚举出的h计算得出该突出高度对应的膨胀后L'值,与题目中给出的L及伸长公式求出的L'相比较,若是求出的L'过大,则说明h取大了,求出的L'过小,说明h取小了。其中需要考虑精度,确定精度的方法和上题分派问题相似,均是将while循环的条件改为right-left>1e-6,但本题需要用到的公式需要一些数学知识(也就是一些我没有的知识)

碎碎念的推导一下:

这是那个万恶的细棒圆弧补充出的半圆

二分专题【二分查找】_第1张图片

由勾股定理R^{2}=(R-h)^{2}+(\frac{L}{2})^{2}可得R=\frac{L^2+4h^2}{8h},此为我们要用到的第一条公式。

因为该段弧长长度L'=2\Theta R,对于R和L/2又有三角函数关系R=\frac{L}{2sin\Theta },变换得\Theta =arcsin\frac{L}{2R},与前式合并可得L'=2R*arcsin(\frac{L}{2R}),此为我们需要用到的第二条公式。

运用这两条公式我们可以求出我们二分出的h对应的L'=...........        公式好复杂,我们还是分开算吧

好了推导完了溜了溜了,留下代码。

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
//#define INF 0x3f3f3f3f
#define N 100005

//R=(L^2+4*h)/(8*h)
//二分枚举h的值,计算出R和L',与题目中的L'进行比较

using namespace std;

int main()
{
	int T;
	int x;
	scanf("%d", &T);
	for(x=1;x<=T;x++)
	{
		double l, n, c;
		scanf("%lf%lf%lf", &l, &n, &c);
		double L, lp;
		double high, low, mid, r;
		L = (1 + n * c)*l;
		high = 0.5*l;
		low = 0;
		while(high-low>1e-6)
		{
			mid = (high + low) / 2;
            //运用推导出的公式求解
			r = (l*l + 4 * mid*mid) / (8 * mid);
			if (2 * r*asin(l / (2 * r)) > L)high = mid;
			else if (lp < L)low = mid;
		}
		printf("Case %d: %.6lf\n", x, mid);
	}
}

 

你可能感兴趣的:(ACM,二分查找,专题,ACM,二分查找)