这套题还是蛮简单的。。。
P1873 [COCI 2011/2012 #5] EKO / 砍树
不难发现锯子越低,砍伐的树木就越长。我们直接二分锯子的高度,然后
O
(
N
)
O(N)
O(N) 代入回去check即可
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
int n,m;
int a[N];
bool check (int x) {
LL sum = 0;
for (int i = 1;i <= n;i++) sum += max (a[i]-x,0);
return sum >= m;
}
int main () {
cin >> n >> m;
for (int i = 1;i <= n;i++) cin >> a[i];
int l = 0,r = N;
while (l < r) {
int mid =( l + r +1 )/2; //记得+1
if (check (mid)) l = mid; //如果当前点合法,那么答案可以更大,并且mid也有可能成为答案,所以l = mid
else r= mid-1; //对应过来的
}
cout << l << endl;
return 0;
}
P2440 木材加工
直接二分答案,对于答案而言,代入回每段木头check,利用整除就能计算出某段木头能切割出多少。本题涉及到无解情况,注意判断。
#include<bits/stdc++.h>
using namespace std;
int A[100005];
int n,k;
bool check(int x){
long long int sum=0;
if(x==0)return true;
for(int i=1;i<=n;i++){
sum=sum+A[i]/x;
if(sum>=k)return true;
}
return false;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}
int L=0,R=1e8;
while(L<R){
int mid=(L+R+1)/2;
if(check(mid)){
L=mid;
}
else{
R=mid-1;
}
}
printf("%d",L);
return 0;
}
P2678 [NOIP2015 提高组] 跳石头
因为起点和终点也是石头,先把两端确定下来,
也就是
D
[
0
]
=
0
,
D
[
N
+
1
]
=
L
D[0]=0,D[N+1]=L
D[0]=0,D[N+1]=L。考虑二分跳跃距离x,直接从左往右枚举石头,计算出相邻石头的跳跃距离并求和,如果该求和距离<x,我们记录石头数量,因为这些石头都是需要被搬走的。如果求和距离大于等于x,那么我们需要重新计算跳跃的距离。这样子能保证我们搬走最少的石头且跳跃的间距尽可能的大。如果最后一段一直跳跃到终点,距离和还是<x,那么需要再搬走一块石头,相当于把上一次的落脚点石头直接移除掉,这样子一步跳跃到终点,满足了“跳跃距离至少为x”
#include<bits/stdc++.h>
using namespace std;
int D[50005];
int n,L,m;
bool check(int x){
int cnt=0;
int len=0;//累计跳跃长度
int need=0;
for(int i=1;i<=n+1;i++){
int dis=D[i]-D[i-1];//石头间距
len=len+dis;
cnt++;
if(i==n+1&&len<x){
need=need+cnt;//把上一次的落脚点也搬走
}
if(len>=x){
need=need+(cnt-1);//cnt-1是因为我们需要一个落脚点石头
len=0;
cnt=0;
}
}
if(need>m)return false;
return true;
}
int main(){
scanf("%d%d%d",&L,&n,&m);
D[0]=0;//起点
D[n+1]=L;//终点
for(int i=1;i<=n;i++){
scanf("%d",&D[i]);
}
int l=1,r=L;
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)){
l=mid;
}
else{
r=mid-1;
}
}
printf("%d",l);
return 0;
}
/*
8 3 1
2
4
7
*/
P1182 数列分段 Section II
二分答案,表示某一段和的最大值x。考虑从左往右计算,依次来凑x。
不要超过x的值,如果超过了应该另起一段,段数++。最终判断生成的段数跟M的关系。如果在当前答案下,段数小于M,说明我们的二分的答案比较大,我们尝试缩小答案; 如果段数等于M,说明我们还有缩小答案的空间;如果段数大于M,说明我们二分的答案比较小,应该放大答案范围
#include<bits/stdc++.h>
using namespace std;
int n;
int A[100005];
int m;
bool check(int x){
int cnt=1;
int s=0;
for(int i=1;i<=n;i++){
if(s+A[i]<=x){
s=s+A[i];
}
else{
s=A[i];
cnt++;
}
}
if(cnt<=m)return true;
else return false;
}
int main(){
cin>>n>>m;
int L=0,R=1e9;
for(int i=1;i<=n;i++){
cin>>A[i];
L=max(L,A[i]);
}
while(L<R){
int mid=(L+R)/2;
if(check(mid)){//假设划分段的和最大值是mid
R=mid;
}
else{
L=mid+1;
}
}
cout<<R;
return 0;
}