二分法专题训练赛

目录

二分法简介

A —Aggressive cows  (POJ 2546)

B — Drying (POJ 3104)

C—Count On Canton (POJ 1969)

G — Expanding Rods (LightOJ 1137)


二分法简介

二分法起源于数学中找函数零点的问题:

对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法。

 

 

如图所示,区间的端点为 A 点和 B 点,函数的图像在区间 AB 范围内是单调递增的,现在要找函数图像的零点 C 。由于这个图像比较特殊,区间 AB 的中点正好是 C 点,所以只需要第一次判断区间中心点就可以找到。假设 D 点是区间的中心,那么可以看出 区间 AD之间的函数值全部小于零,因此全部不满足条件,区间 DB中,函数连续且单调递增,左半边函数值小于零,右半边函数值大于零,那么在 DB 之间必然存在一个位置 使得函数值等于零。也就意味着以 D 点为分界,左半边 一定没有答案,右半边一定有答案。这样不断缩小区间,不断逼近,一定能让包含答案的区间长度足够小,比题目要求的精度还要小,这个时候,区间内的任意一个实数都可以作为答案。使用这种方法求函数零点的条件是:在区间范围内,函数图像连续且单调。

现在将这种二分的思想运用在程序设计中,也能产生神奇的效果。

二分法的使用条件:

  1. 待查找的序列区间单调有序(单调递增或单调递减都可以)

  2. 待查找序列和题目的要求建立的函数关系单调有序

也就是说,在将区间一分为二后,答案只能出现在其中的一个区间,不能同时出现在两个区间内,这样的情形下才适合二分。如果不能保证这个条件,那就需要考虑别的方法。

二分法使用方法:

假设待查找序列和题目的要求之间的关系是单调递增的,先取区间的中心,判断该处函数值和题目标准值的大小关系,如果函数值偏小,那么应该在中心右侧的区间继续查找;如果函数值偏大 ,那么应该在中心左侧区间继续查找,直到找到对应的值或者区间缩小到左右端点之间不再包含其他数据结束。

A —Aggressive cows  (POJ 2546)

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

题目大意: 

这题理解题意花了很长时间,看了好久题目又问了同学才终于理解了题意。就是给你N个牛栏每个牛栏和起点的距离已知,农夫要在这些牛栏中放入C头牛,要求求出所有放牛的方案中每两头牛之间距离最小值的最大值.

想法:

二分专题自然要首先往二分法的方向想,但是这道题要怎么二分呢,对什么进行二分呢,这是我们首先要明确的问题。题目要求我们求出最小距离的最大值,那自然是对距离进行二分,要进行二分那就要找到左端点和右端点,我们可以先取左端点为0,右端点为整个牛栏的长度,所以我们就可以得到这两端的中间值mid,这个mid就是我们准备将牛放进去的最小间隔。放的方法就是先在第一个牛栏中放一头牛,然后判断第二个牛栏和第一个牛栏的距离是不是大于等于这个mid,如果不是就不放,继续判断下一个位置是不是符合条件,如果是就往里面放一头牛,下次判断距离时应从最近的有牛的位置判断。这样我们在每个符合条件的位置上都放了一头牛,遍历完所有牛栏后我们应该检查放入牛的个数和提供的牛个数是否相同,如果放入的多了,即符合条件的位置多于牛的个数,说明mid不是最大值还可以再大一点,于是要进行第二次二分,让left = mid+1(因为最大的mid一定在原来的mid和right之间),更新mid的值为新区间的中点,直到获得的符合条件的位置与牛的个数相同。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1e9 + 5;
const int maxn = 1e5 + 5;
int n, k, x[maxn];
int check(int d)   //这里一开始可能想不出来。。
{
    int last = 0;
    for(int i = 1; i < k; i++)  //这里写的很秒
    {
        int cur = last + 1;   //用两个变量“比”着判断
        while(cur < n && x[cur] - x[last] < d)
            cur++;
        if(cur == n) 
			return 0;
        last = cur;
    }
    return 1;
}
int main()
{
    while(~scanf("%d%d", &n, &k))
    {
        for(int i = 0; i < n; i++)
            scanf("%d", &x[i]);
        sort(x, x+n);
        int l = 0, r = INF;
        while(r - l > 1)
        {
            int mid = (l + r) / 2;
            if(check(mid)) 
				l = mid;
            else 
				r = mid;
        }
        printf("%d\n", l);
    }
    return 0;
}

 

B — Drying (POJ 3104)

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 ≤ 10^9). The third line contains k (1 ≤ k≤ 10^9).

Output

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

Sample Input 

3 
2 3 9 
5 

3 
2 3 6 
5

Sample Output

3 

2

题目大意:

Jane有n件衣服需要晒干,每件衣服上有一定的水分,在自然条件下每过一分钟每件衣服上的水分就会减少1,现在Jane有一个烘干机,但是这个烘干机有点小,每次只能烘一件衣服,在上面放一分钟就能使衣服上的水分减少k(水分不会小于0),Jane是一个聪明的girl她每次都能采取最佳策略在最短时间内把所有衣服烘干,请你求出这个最少时间。

想法:

这道题我看完以后感觉跟二分法好像没啥关系,以为每次只要贪心地烘干含水量最大的衣服就好了,结果写完代码测试了一下全是bug,样例都过不了,后来改了半天发现代码没问题但是测试数据都是错的,就知道这样做不行了,毕竟是二分专题,还是乖乖用二分法做吧。这道题要求的是时间,那么就应该对时间二分,左端点left定为1,右端点right定为含水量最大衣服的自然风干时间,得到中间值mid,然后检查一下在经过mid的时间后没干的衣服的含水量,然后从一开始就对后来没干的衣服进行烘烤,看能不能在mid时间到达之前把无法自然干的衣服全部烘干,因此需要定义一个计数器cnt记录烘干的次数,若cnt>mid说明答案在右区间,应该继续对右区间二分,若cnt<mid说明答案在左区间,继续对左区间二分。要注意cnt=mid时还不一定是答案,还要把mid减小试一下,所以还要再在左区间二分一下,最后的答案是左端点值。

AC代码:

#include <iostream>
#include <stdio.h>
#include  <algorithm>
#include <cmath>
 
using namespace std;
 
int main()
{
    long long flag,k,i,a[100004],left,right,mid,maxnum,n,t,sum=0;
    while(scanf("%lld",&n)!=EOF)
    {
    	flag=0;
    	sum=0;
    	maxnum=-1;
    for (i=0;i<=n-1;i++)
    {
        scanf("%lld",&a[i]);
        if (a[i]>maxnum)
        {
            maxnum=a[i];     //记录最大含水量,作为右端点值
        }
    }
    scanf("%lld",&k);
    if (k==1)
    {
        printf("%lld\n",maxnum);
        flag=1;
    }
    sort(a,a+n);       //含水量从小到大排序
    left=1;
    right=maxnum;
    while(right-left>0&&flag==0)
    {
        mid=(left+right)/2;
        sum=0;
        for (i=0;i<=n-1;i++)
        {
            if (a[i]>mid)
            {
                t=ceil((a[i]-mid)*1.0/(k-1));  //用烘干机的次数(ceil函数用来获得不小于括号中值的数)
                sum=sum+t;   //记录总时间 
            }
        }
        if(sum>mid)   //总时间比中间值大,说明答案在右半区间
        {
            left=mid+1;
        }
        else
        {
            right=mid;  //答案在左半区间的情况
        }
    }
    	mid=(right+left )/2;
    if(flag==0)
    {
        printf("%lld\n",mid);
    }
    }
    return 0;
}

 

C—Count On Canton (POJ 1969)

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,从上往下分子+1,并且按蛇形规律编号,第一个是1/1,第二个是1/2,第三个是2/1,第四个是3/1,第五个是2/2,……要求输入一个位置,输出这个位置上的数。

想法:

这题刚开始我是想用二分法做的,想了半天还是不知道怎么跟二分法搭上边,算了还是找规律吧。在草稿纸上写了大概五六行,规律就差不多找到了。先定义一个sum = 0,让sum+1,+2,+……直到sum大于或等于n,这样做是为了确定第n个位置所在的层数,用cnt记录最后一个加的数,然后比较sum和n的大小,如果sum == n,说明这个位置一定在生成数阵的边界上,然后根据cnt%2的值判断是在奇数层还是偶数层,输出1/cnt或cnt/1。如果sum>n的话,还是要先判断层数的奇偶性,然后根据sum和n的差来对分子和分母进行修正,最后输出即可。

AC代码:

#include <iostream>

using namespace std;

int main()
{
    int n;
    while (~scanf("%d",&n)){
    	int sum = 0; 
    	int cnt;   //记录层数
    	int p,q;   //分子和分母
		for (int i = 1; i<n; i++){  //得出n所在的层数
			sum = sum + i;
			cnt = i;
			if (sum>=n){
				break;
			}
		}
		if (sum == n){          //sum == n说明这个位置在边界上
			if (cnt%2 == 0){    
				printf("TERM %d IS %d/1\n",n,cnt);
				continue;
			}
			else {
				printf("TERM %d IS 1/%d\n",n,cnt);
				continue;
			}
		}
		    //不在边界上的情况
			if (cnt%2 == 1){  //奇数层的情况
				p = 1;
				q = cnt;
				while (sum != n){
					sum--;
					p++;
					q--;
				}
				printf("TERM %d IS %d/%d\n",n,p,q);
				continue;
			}
			if (cnt%2 == 0){   //偶数层的情况
				p = cnt;
				q = 1;
				while (sum != n){
					sum--;
					p--;
					q++;
				}
				printf("TERM %d IS %d/%d\n",n,p,q);
				continue;
			}
		
	}
    return 0;
} 

G — Expanding Rods (LightOJ 1137)

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

题目大意:

这题就是一个初中几何题,简单来说就是已知弧长和弦长要你求拱高,相关公式题目中都已给出。

想法:

由勾股定理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}),此为我们需要用到的第二条公式。然后列出方程用二分法求方程近似解,达到题目要求的精度即可。

AC代码:

#include<bits/stdc++.h>


using namespace std;
#define esp 1e-7     //精度

double pi = acos(-1);
double L,l1;

bool check(double mid)      //判断答案在左区间还是右区间的函数
{
	double ans = 2 * asin((L / 2) / ((L*L + 4 * mid*mid) / (8 * mid)))*((L*L + 4 * mid*mid) / (8 * mid));
	return ans >= l1;
}

int main()
{
    int T;
    int i = 1;
    scanf("%d",&T);
    while (T--){
    	double n,c,m;
    	double h,h1 = 0,h2;
    	scanf("%lf%lf%lf",&L,&n,&c);
    	h2 = L/2;              //左端点
    	l1 = (1+n*c)*L;        //右端点
       	
    	while (h2-h1>esp){      //二分法求方程近似解直到精度符合要求
    		
			m = (h1 + h2) / 2;
			if (check(m))
			{
				h2 = m;
			}
			else
			{
				h1 = m;
			}

		}
		
		printf("Case %d: %lf\n",i,h2);  //输出答案 
		i++;
	}
    return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值