题意:
给定一张图,在图上的瓷砖上进行涂色,共红、蓝两种颜色。需要给a个瓷砖涂红色,b个瓷砖涂蓝色。
要求最后涂完之后,a+b块瓷砖能够组成一个矩形,并且红色部分或者蓝色部分需要组成一个矩形。
求该条件下矩形的最小周长。
思路:
一开始拿到该题,想的是既然面积是确定的,需要求边长,那是不是可以考虑基本不等式进行一下计算。然后便发现这种思路错的还是挺离谱的,并且样例都过不了。
然后看了题解之后发现,原来可以进行暴力枚举。这里需要注意一下,a和b的范围是10^14,因此枚举 a 和 b 的因数,只需要枚举到 sqrt(a) 即可,因此范围是1e7,刚好可以O(n)复杂度过去。
这里需要注意一下,a在sqrt(a)之内的因数个数不会超过a^(1/3),这里可以用于开数组大小。
然后具体的算法就是,我们需要进行两次操作,第一次假定 a 拼接成内部的小矩形,第二次假定 b 拼接成内部的小矩形。
然后进入 solve(ll x, ll y) 函数之后,先将 x 的所有除数都求出来存放到一个数组中,然后再对于 y 进行除数分解,每次分解到一个因数的时候,需要枚举x除数的数组中所有小于这个因数的数,然后对ans进行一次更新即可求出答案。
反思:
第一次看到这个题的时候还是没想出来具体该怎么做,或许和时间短有关系,但是不能因为时间短就找理由,还是自己实力不济。
第一眼想到的就是O(1)算法还是微迷的,仔细看看数据范围,应该先从O(n)算法着手,然后再去思考能不能进行因数枚举进行暴力遍历求出结果。嗯,正确的思路应该是这样的。
所以,还是应该继续努力啊!
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
typedef long long ll;
const ll N = 1e6;
ll ha[N],hb[N];
int ta,tb;
ll solve(ll a,ll b)
{
ll ans = 2*(a+b)+2;
ta = tb = 0;
for(ll i = 1; i*i <= a; i++)
{
if(a%i == 0) ha[ta++] = i;
}
int l = 0;
for(ll i = 1; i*i <= (a+b); i++)
{
l = 0;
if((a+b)%i == 0)
{
while(ha[l] <= i && l < ta)
{
if((a/ha[l]) <= (a+b)/i) ans = min(ans,2*(i+(a+b)/i));
l++;
}
}
}
return ans;
}
int main()
{
ll a,b;
while(~scanf("%I64d%I64d",&a,&b))
{
ll ans = min(solve(a,b),solve(b,a));
printf("%I64d\n",ans);
}
return 0;
}