二分法专题练习

目录

Pie

Monthly Expense

River Hopscotch

进击的奶牛

借教室

跳石头

聪明的质监员


Pie

 POJ - 3122 

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

InputcopyOutputcopy
3
3 3
4 3 3
1 24
5
10 5
1 4 2 3 4 5 6 5 4 2
25.1327
3.1416
50.2655

思路

N块蛋糕,使F+1人分到的蛋糕平均且最大(每个人拿到的蛋糕不可以是多块蛋糕拼凑的) 


#include<math.h>
#include<stdio.h>
int t,n,f;
double a[10005];//每块pie的大小 
double PI=acos(-1.0);//反余弦 3.141592653589793
#define eps 1e-7
int main()
{
    scanf("%d",&t);
    while(t--){
    	scanf("%d%d",&n,&f);f++;//加上主人 
    	double maxa=0;
    	for(int i=0;i<n;i++){
    		int r; scanf("%d",&r);
    		a[i]=PI*r*r;
    		if(maxa<a[i]) maxa=a[i];
			}
		double l=0,r=maxa;
		while(r-l>=eps){
			double mid=(r+l)/2;
			int sum=0;
	        for(int i=0;i<n;i++){
		        sum+=(int)(a[i]/mid);
	        }
			if(sum>=f) l=mid;//可分的蛋糕数大于或人数,则可能不是最优解
			else r=mid;
		}	
		printf("%.4lf\n",l);	
	}
 return 0;	
} 

 

Monthly Expense

 POJ - 3273 

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

InputcopyOutputcopy
7 5
100
400
300
100
500
101
400
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.

 题目大意

N天中每天的花费,分成连续的M组,要求各组的花费之和应该尽可能地小,输出各组花费之和中的最大值


#include<cstdio> 
#include<iostream>
#include<algorithm>
using namespace std;
int A[100001];
int n,m;
bool check(int x) 
{
    int c=1;
    int ans=A[1];
    if(ans>x) return 0;
    for(int i=2;i<n;i++){
        if(A[i]>x) return 0;
        if(ans+A[i]<=x)ans+=A[i];
        else{
            c++;
            ans=A[i];
        }
    }
    if(c>m) return 0;
    else return 1;
}
int main()
{ 
  scanf("%d%d",&n,&m);
  int r=0,l=-1;
  for(int i=1;i<=n;i++){
  	scanf("%d",&A[i]);
  	r+=A[i];//右界为数组总和 
  	l=max(l,A[i]);//左界从数组最大值开始
  }
  while(l<r){
      int mid=(l+r)/2;
      if(check(mid)) r=mid;//可行,继续查找更优解 
      else l=mid+1;//不可行
  }
  printf("%d\n",l);
  return 0;
} 

River Hopscotch

 POJ - 3258

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, L units 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

InputcopyOutputcopy
25 5 2
2
14
11
21
17
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).

题目大意

湖中心离岸边L,N个石块,抽走M块,使得跨越石块最大距离最小

#include<cstdio> 
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
ll A[500051];
ll l,n,m;
const ll inf = 9999999999;
ll work(ll l,ll r) //二分枚举
{
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        int ans=0,s=0;
        for(int i=1; i<n; i++)
            if(A[i]-A[s]>=mid)//距离大于 mid 
                s=i;
            else ans++; //删去的石块数
        if(ans>m)  //若数量大于m,跳跃距离太大
            r=mid-1;
        else l=mid+1;
    }
    return r;
}

int main()
{ 
    while(~scanf("%lld%lld%d",&l,&n,&m)){
        A[0]=0,A[n+1]=l;
        for(int i=1;i<=n;i++)
            scanf("%d",&A[i]);
            n+=2;
        sort(A,A+n);//排序 
        ll minn=inf;
        for(int i=1; i<n; i++)
           minn=min(minn,A[i]-A[i-1]); 
           printf("%lld\n",work(minn,l));
    return 0;
    }
} 

 

进击的奶牛

 洛谷 - P1824 

Description

Farmer John 建造了一个有 N(2≤N≤10^5) 个隔间的牛棚,这些隔间分布在一条直线上,坐标是 x1​,x2​,⋯,xN​(0≤xi​≤10^9)。

他的 C(2≤C≤N)头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John 想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?

Input

第 11 行:两个用空格隔开的数字 N 和 C。

第 2∼N+1 行:每行一个整数,表示每个隔间的坐标。

Output

输出只有一行,即相邻两头牛最大的最近距离。

Sample 1

InputcopyOutputcopy
5 3
1
2
8
4
9
3

#include<cstdio> 
#include<iostream>
#include<algorithm>
using namespace std;
int A[100001];
int n,c,a;
bool check(int x) 
{
    int num=0;
    int l=A[1];//l记录上一只牛的位置
    for(int i=2;i<=n;i++) 
    {
        if(A[i]-l<x) num++;
        else l=A[i];
        if(num>a) return false;//不可行 
    }
    return true;//可行
}
int main()
{ 
  scanf("%d%d",&n,&c);
  for(int i=1;i<=n;i++)
    scanf("%d",&A[i]);
  sort(A+1,A+n+1);//排序
  a=n-c;//最大剩余牛栏数 
  int l=1;//左界从1开始 
  int r=A[n]-A[1];//右界 
  while(l+1<r) 
  {
      int mid=(l+r)/2;
      if(check(mid)) l=mid;//可行,继续查找更优解 
      else r=mid;//不可行
  }
  if(check(r)) printf("%d\n",r);//若可行解为右界
  else printf("%d\n",l);//若可行解为左界
  return 0;
} 

 

借教室

 洛谷 - P1083 

Description

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来 n 天的借教室信息,其中第 i 天学校有 ri​ 个教室可供租借。共有 m 份订单,每份订单用三个正整数描述,分别为 dj​,sj​,tj​,表示某租借者需要从第sj​ 天到第 tj​ 天租借教室(包括第 sj​ 天和第 tj​ 天),每天需要租借dj​ 个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 dj​ 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 sj​ 天到第 tj​ 天中有至少一天剩余的教室数量不足 dj​ 个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

Input

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

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

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

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

Output

如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足)

输出两行,第一行输出一个负整数 −1,第二行输出需要修改订单的申请人编号。

Sample 1

InputcopyOutputcopy
4 3 
2 5 4 3 
2 1 3 
3 2 4 
4 2 4
-1 
2
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define maxn 1000005
typedef  long long ll;
using namespace std;

ll L,M,R,n,m,r[maxn],d[maxn],s[maxn],t[maxn],day[maxn];

bool judge(ll mid)
{
    memset(day,0,sizeof(day));
    for (ll i=1;i<=mid;i++){
        day[s[i]]+=d[i];
        day[t[i]+1]-=d[i];
    }
    if (day[1]>r[1]) return 1;
    for (ll i=2;i<=n;i++){
        day[i]+=day[i-1]; //订单数超出可借范围
        if (day[i]>r[i])return 1;
    }
    return 0;
}

int main()
{
    cin >> n >> m;
    for (ll i=1;i<=n;i++)
        scanf("%lld",&r[i]);
    for (ll i=1;i<=m;i++)
        scanf("%lld%lld%lld",&d[i],&s[i],&t[i]);
    L=1;R=m;
    while (L<R){
        M=(L+R)/2;
        if (judge(M)) R=M;
        else L=M+1;
    }    
    if (m!=R) cout <<-1<<endl<<R<<endl;
    else cout<<0<<endl;
    return 0;
}

 

跳石头

 ​​​​​​洛谷 - P2678

Background

一年一度的“跳石头”比赛又要开始了!

Description

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

Input

第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1 且 N≥M≥0。

接下来 N 行,每行一个整数,第 i 行的整数 Di​(0<Di​<L), 表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

Output

一个整数,即最短跳跃距离的最大值。

Sample 1

InputcopyOutputcopy
25 5 2 
2
11
14
17 
21
4

Hint

输入输出样例 1 说明

将与起点距离为 2和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

 


#include<cstdio> 
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
ll A[500051];
ll l,n,m;
const ll inf = 9999999999;
ll work(ll l,ll r) //二分枚举
{
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        int ans=0,s=0;
        for(int i=1; i<n; i++)
            if(A[i]-A[s]>=mid)//距离大于 mid 
                s=i;
            else ans++; //删去的石块数
        if(ans>m)  //若数量大于m,跳跃距离太大
            r=mid-1;
        else
            l=mid+1;
    }
    return r;
}

int main()
{ 
    while(~scanf("%lld%lld%d",&l,&n,&m)){
        A[0]=0,A[n+1]=l;
        for(int i=1;i<=n;i++)
            scanf("%d",&A[i]);
            n+=2;
        ll minn=inf;
        for(int i=1; i<n; i++)
           minn=min(minn,A[i]-A[i-1]); 
           printf("%lld\n",work(minn,l));
    return 0;
    }
} 

聪明的质监员

 洛谷 - P1314 

Description

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1 到 n 逐一编号,每个矿石都有自己的重量 wi​ 以及价值 vi​ 。检验矿产的流程是:

1 、给定m 个区间 [li​,ri​];

2 、选出一个参数 W;

3 、对于一个区间 [li​,ri​],计算矿石在这个区间上的检验值 yi​:

yi​=j=li​∑ri​​[wj​≥W]×j=li​∑ri​​[wj​≥W]vj​

其中 j 为矿石编号。

这批矿产的检验结果 y 为各个区间的检验值之和。即:1∑m​yi​

若这批矿产的检验结果与所给标准值s 相差太多,就需要再去检验另一批矿产。小T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近标准值 s,即使得 ∣s−y∣ 最小。请你帮忙求出这个最小值。

Input

第一行包含三个整数n,m,s,分别表示矿石的个数、区间的个数和标准值。

接下来的 n 行,每行两个整数,中间用空格隔开,第i+1 行表示 i 号矿石的重量 wi​ 和价值 vi​。

接下来的 m 行,表示区间,每行两个整数,中间用空格隔开,第 i+n+1 行表示区间 [li​,ri​] 的两个端点 li​ 和 ri​。注意:不同区间可能重合或相互重叠。

Output

一个整数,表示所求的最小值。

Sample 1

InputcopyOutputcopy
5 3 15 
1 5 
2 5 
3 5 
4 5 
5 5 
1 5 
2 4 
3 3 
10

Hint

【输入输出样例说明】

当 W 选 4 的时候,三个区间上检验值分别为 20,5,0 ,这批矿产的检验结果为 25,此时与标准值 S 相差最小为 10。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int w[maxn],v[maxn],l[maxn],r[maxn];
long long pre_n[maxn],pre_v[maxn];
long long Y,s,sum;
int n,m,mx=-1,mn=2147483647;
bool check(int W)
{	
	Y=0,sum=0;
	memset(pre_n,0,sizeof(pre_n));
	memset(pre_v,0,sizeof(pre_v));
	for(int i=1;i<=n;i++)
	{
		if(w[i]>=W) pre_n[i]=pre_n[i-1]+1,pre_v[i]=pre_v[i-1]+v[i];
		else pre_n[i]=pre_n[i-1],pre_v[i]=pre_v[i-1];
	}
	for(int i=1;i<=m;i++)
		Y+=(pre_n[r[i]]-pre_n[l[i]-1])*(pre_v[r[i]]-pre_v[l[i]-1]);
		

	sum=llabs(Y-s);
	if(Y>s) return true;
	else return false;
	
}
int main(){
	scanf("%d %d %lld",&n,&m,&s); 
	for(int i=1;i<=n;i++)
	{
		scanf(" %d %d",&w[i],&v[i]);
		mx=max(mx,w[i]);
		mn=min(mn,w[i]);	
	}
	for(int i=1;i<=m;i++)
		scanf(" %d %d",&l[i],&r[i]);
	int left=mn-1,right=mx+2,mid; 
	long long ans=0x3f3f3f3f3f3f3f3f;
	while(left<=right)
	{
		mid=(left+right)>>1;
		if(check(mid)) 	left=mid+1;
		else right=mid-1;
		if(sum<ans) ans=sum;
	}
	printf("%lld",ans);
	return 0;
} 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值