二分4——二分答案
T1 营救
题目:
一座摩天大楼起了大火,n个人都被困在了顶层狭长的走廊上,大家排着长长的队伍等着逃离险境。但火势很猛,消防员升起的救生舱只有m次运人下来的机会,并且每次运的人总重量还不能太重,避免将救生舱压垮。
此时如何将这一排人分隔成m个连续的小组,(大家遵守逃生守则,没有人会往前插队),并且让这m个组中总重量最重的那个组的重量尽量小。这样才能快速安全的将大家都救离险境。
现在告诉你这n个人的体重,请你找出一种分组方法,让这m个组中总重量最重的那个组的重量尽量小,并输出这个组的总重量。
输入
第一行 两个正整数n和m,中间用一个空格隔开,表示有n个逃生的人和要分隔成m个连续的小组。
第二行 。n个正整数,每个整数之间用一个空格隔开,表示n个人的体重(单位:公斤,不超过10^8)。
输出
一个正整数,表示m个组中总重量最重的那个组的重量。
输入样例
6 3
20 30 50 80 100 120
输出样例
180
思路:
因为这道题要求的是最重的那个组的重量,所以我们二分的范围就是n个人中最重的那个人的重量到总重。接下来就一直做二分,每次都列举一个x,然后循环n遍,看在最多承载x重量的情况下能分成几组,如果符合要求,则将x往大求,否则将x往小求。
PS:一定要注意变量类型,千万不能开小了。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,a[1000010],t,y,z;
bool chk(long long x)
{
long long s=0,k=1;
for(int i=1;i<=n;i++)
{
s+=a[i];
if(s>x)
{
k++;
s=a[i];
}
}
return k<=t;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>t;
for(int i=1;i<=n;i++)
{
cin>>a[i];
y+=a[i];
z=max(z,a[i]);
}
while(z<y)
{
long long m=(z+y)/2;
if(chk(m))
y=m;
else
z=m+1;
}
cout<<y;
return 0;
}
T2 隔离
题目:
由于机场是人员密集场所,在疫情期间很容易导致病毒扩散传播,因此M国在机场附近建设了 n (2≤n≤100,000) 个隔离点,随时准备隔离从飞机上下来的疑似乘客。
为了方便管理,这些隔离点都是分布在一条直线上,每个隔离点都有一个坐标 xi (0≤xi≤1,000,000,000)进行标记。
现在发现有 c (2≤c≤n) 人出现疑似症状,需要进行隔离,为了最大程度保证安全,希望使得任意两个相邻隔离者间的最短距离尽量长,请你计算这个最长的最短距离为多少?
输入
第1行:两个用空格隔开的整数 n 和 c。
第2到n+1行:每行包括一个整数,表示隔离点的位置xi。
输出
一个整数,表示最长的最短距离。
输入样例
5 3
1 2 8 4 9
输出样例
3
思路:
这道题目要求的是:如何安排使得这样隔离中距离最近的两个人之间的距离是所有方案中最长的。
那么就可以想到,第一个人一定是安排在第一个隔离点的,而最远的距离就是第一个隔离点和最后一个隔离点的距离(y),因此二分查找的范围就是1~y,二分找的是隔离点之间最短的距离(x),然后二分,如果符合要求,则说明比x还短的所有距离都是符合的,所以要把查找范围向大缩,否则就往小缩。在chk的时候要注意将上一个人的位置存下来§,然后每次都比较一下当前隔离点与上一个人的距离是否大于x,如果是的话要将p存下来改成当前点,再把可以隔离的人数+1,循环结束后再比较能隔离的人数是否符合要求。
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,a[1000010],t,y,z;
bool chk(long long x)
{
long long s=0,k=1,p=a[1];
for(int i=2;i<=n;i++)
{
s=a[i]-p;
if(s>=x)
{
p=a[i];
k++;
}
}
return k>=t;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>t;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n);
y=a[n]-a[1];
z=1;
while(z<y)
{
long long m=(z+y+1)/2;
if(chk(m))
z=m;
else
y=m-1;
}
cout<<y;
return 0;
}