题意:一个n*m的长方形,可以沿水平或竖直方向画若干条线,每条线的两端点都在长方形边界上,且线要与长方形的某一边平行且端点位于格点上。要求这些线划分出的每个小长方形面积都大于等于k,求最多可以画几条线。
分析:先来看这么一个问题:一根长为n的绳子,我们要用剪刀将其剪断,要保证每段长度不能小于m,求最多能剪几次?
答案是n/m-1,而不是n/m的下取整,因为我们要保证每一段长度都不能小于m,所以如果剪完最后一次后会使得最后剩余一段长度小于m的绳子,那么这一次我们是不能剪的,所以就需要减1,知道了这个我们再来分析一下这个问题:
我们可以来O(min(k,n))枚举矩形的高h,那么对于每次的高h,为了使得矩形的面积不小于k,那么至少需要的宽度就是w=(k-1)/h+1,那么为了使得切的次数是最大的,那么我们显然宽度越小越好,因为我们切的次数是m/w-1,这显然是一个单调函数,随着w的增大而减小,所以对于每个高已经固定的情况,我们都能O(1)确定切的次数,所以总的复杂度就是O(min(k,n)),对于高为h,宽为b的情况,我们切的次数最多是(n/h+m/b-2),需要注意的是我们的h是小于n的,这个我们可以在for循环的时候就直接设定边界,而b是小于m的,这个需要我们特判一下,细节见代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
long long n,m,k;
long long cal(long long h)
{
long long ans=n/h-1;
long long b=(k-1)/h+1;//求解最小的b满足h*b>=k
if(b>m) return 0;
return (ans+(m/b-1));
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%lld%lld%lld",&n,&m,&k);
long long ans=0;
for(int i=1;i<=min(k,n);i++)
ans=max(ans,cal(i));
cout<<ans<<endl;
}
return 0;
}