题目链接
题目大意:
一段长度为n的路上分布着石头,让m个同学从0开始去搬石头。每个同学每秒钟可以向右移动一步,或者移除当前位置的一块石头,问最少需要多少时间能搬完所有的石头
做法一:
二分时间t,然后让一个人工作t时间(有石头就搬,没有就向右走),如果工作未完成继续派出一个人去工作t时间。
#include<bits/stdc++.h>
#define ll long long
#define IOS {ios::sync_with_stdio(0);cin.tie(0);}
using namespace std;
const int N = 1e5+1000;
int n,m,a[N],k,old[N];
ll min(ll a,ll b){
if(a <= b) return a;
else return b;
}
bool ok(ll ans,int a[]){
ans --;
int i,pos=1;
int use = 1,sub = 0;
ll last=ans;
while(1){
if(sub==k) return 1;
if(last==0) {last = ans-pos+1;use++;}
if(use>m) return 0;
ll cost;
if(a[pos]>0){
cost = min(a[pos],last);
a[pos] -= cost;
last -= cost;
if(a[pos]==0) sub++;
continue;
}
if(a[pos]==0){
pos++;
last--;
}
}
}
int main(){
// freopen("a.txt","r",stdin);
IOS;
int i;
cin>>n>>m;
for(i = 1;i <= n;i ++){
cin>>a[i];
if(a[i]>0) k ++;
}
memcpy(old,a,sizeof(old));
ll l,r;
l = 1; r = 1e18;
while(l+1<r){
ll mid = l+r>>1;
memcpy(a,old,sizeof(a));
if(ok(mid,a)) r = mid;
else l = mid;
}
memcpy(a,old,sizeof(a));
if(ok(l,a)) cout<<l;
else cout<<r;
return 0;
}
做法二:
二分时间t,显然最后一秒一定在搬最右边的石头。设最右边的坐标为pos,那么需要在time=t-pos+1的时间内搬完这堆石头。至少需要派出need=ceil(a[i]/(t-pos+1))个人去搬,在路上最多可以耽误need*time-a[i]的时间,这些时间可以用来移除前面遇到的石头。
#include<bits/stdc++.h>
#define ll long long
#define IOS {ios::sync_with_stdio(0);cin.tie(0);}
using namespace std;
const int N = 1e5+1000;
int n,m,a[N],k,old[N];
ll min(ll a,ll b){
if(a <= b) return a;
else return b;
}
bool ok(ll ans,int a[]){
int i;
ans --;
ll use=0,last=0;
for(i = n;i >= 1;i --){
ll p;
p = min(a[i],last);
a[i]-=p;
last-=p;
if(a[i]==0) continue;
if(ans-i+1<=0) return 0;
ll k = ceil(1.0*a[i]/(ans-i+1));
use += k;
last += ((ans-i+1)*k)-a[i];
}
if(use>m) return 0;
return 1;
}
int main(){
freopen("a.txt","r",stdin);
IOS;
int i;
cin>>n>>m;
for(i = 1;i <= n;i ++){
cin>>a[i];
if(a[i]>0) k ++;
}
memcpy(old,a,sizeof(old));
ll l,r;
l = 1; r = 1e18;
while(l+1<r){
ll mid = l+r>>1;
memcpy(a,old,sizeof(a));
if(ok(mid,a)) r = mid;
else l = mid;
}
memcpy(a,old,sizeof(a));
if(ok(l,a)) cout<<l;
else cout<<r;
return 0;
}