假设一个人要点a
个饼干和b
个松饼,并且设要升级饼干x
次、升级松饼y
次,那么我们知道,想让这个人不生气,需要满足:
a(tc−x)+(tm−y)≤c
依次遍历每个人即可,求出所有满足这个不等式的最小的升级次数
然而这个不等式有两个未知量,难以求解。为了解决问题,我们可以想办法把多个未知数转化成一个未知数
我们知道总升级次数为x+y,因此我们可以令x+y=mid⟹y=mid−x,这样这个不等式就变成了:
a(tc−x)+b(tm−mid+x)≤c⟹(b−a)x≤c−atc−b(tm−mid)
由于制作的最小时间为1,所以mid
的最大值就是tc+tm−2
因此又能推出两个不等式,分别为:
0≤x≤tc−1
和
0≤y≤tm−1⟹mid+1−tm≤x≤mid
即对于每个顾客,满足此不等式组:
{(b−a)x≤c−atc−b(tm−mid)0≤x≤tc−1mid+1−tm≤x≤mid
这样,未知数只有mid
,并且在规定的范围内,mid
也就是升级次数越多,等待的时间越短,越能够满足不等式组。因此mid
具有单调性,可以直接二分答案
#include <iostream>
#include <cmath>
using namespace std;
const long long N = 110;
long long t, n, tc, tm;
long long a[N], b[N], c[N];
// 如果发现不等式无法满足,则表示升级次数不够,需要提高l
// 如果不等式可以或恰好可以满足,则表示升级次数已经可以达到,需要降低r来找最少升级次数
bool check(long long mid)
{
long long minx = max(0ll, mid + 1 - tm), maxx = min(tc - 1, mid);
for (long long i = 0; i < n; i++)
{
long double l = b[i] - a[i];
long double r = c[i] - (long double)a[i] * tc - (long double)b[i] * (tm - mid);
if (l == 0 and r < 0)
return true;
else if (l > 0)
maxx = min(maxx, (long long)floor(r / l));
else if (l < 0)
minx = max(minx, (long long)ceil(r / l));
}
return minx > maxx;
}
int main()
{
cin >> t;
while (t--)
{
cin >> n >> tc >> tm;
for (long long i = 0; i < n; i++)
cin >> a[i] >> b[i] >> c[i];
// 不需要判断加减1的二分模板,起始位置要-1,终点要+1
long long l = -1, r = tc + tm - 1;
while (l + 1 != r)
{
long long mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
cout << r << '\n';
}
return 0;
}