一. 题意
给定n个数,每一次能使一个数减少A,其它数减少B。求最小的次数,使得所有数小于等于0.
二. 思路
我们有一个很直白的贪心思路:
- 遍历序列,获取当前的最大数。
- 让最大数减A,其余减B。
于是,0分的代码有啦!!!(连样例都不能过)
#include<iostream>
#include<bits/stdc++.h>
#define int long long
#define rint register int
#define de(x) cout<<#x<<" "<<x<<endl;
using namespace std;
const int N=100000+100,INF=0x3f3f3f3f;
int n,A,B,ans,a[N];
signed main()
{
cin>>n>>A>>B; A-=B;
for(int i=1;i<=n;i++) cin>>a[i];
while(1)
{
int* Tp=max_element(a+1,a+n+1);
if(*Tp<=0) break;
else
{
for(int i=1;i<=n;i++)
{
if(a[i]<=0) continue;
a[i]-=B;
}
Tp-=A;
ans++;
}
}
cout<<ans<<endl;
return 0;
}
思考:为什么不对?
发现:答案具有单调性。准确而言,如果次数k满足条件,那么>k的就一定能行。
摊牌了,这题就是二分。
我们每一次选取一个mid,判断当前的mid是否合法。
判断函数:
我们先遍历整个序列,将每个数减去 mid*B。如果此时的数还大于0,则补上若干个(B-A),时起值小于等于0,本次产生的花费就是减去的次数。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,A,B,a[N],b[N];
bool ck(int mid)
{
//先将原数组 copy 一遍,因为原数组不能被修改,修改的话只能新开个数组。
for(int i=1;i<=n;i++) b[i]=a[i];
//res记录次数
int res=0;
for(int i=1;i<=n;i++)
{
//判断当前是否要用A技能
if(b[i]<=B*mid) continue;
b[i]-=B*mid;
//如果要,则增加次数。
res+=ceil(b[i]*1.00/A);
}
if(res>mid) return false;
else return true;
}
signed main()
{
//读入加速
cin.sync_with_stdio(0);cin.tie(0);cout.sync_with_stdio(0);cout.tie(0);cin.exceptions(cin.failbit);cout.exceptions(cout.failbit);
cin>>n>>A>>B;
for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
A-=B;
//注意边界
int int l=1,r=1e10;
while(l<r)
{
int int mid=(l+r)/2;
if(ck(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}