100分解法
本题算是一道构造题。首先对于题中n个棋子对气的定义,显然n个棋子气的上限是4*n,但是我们不是在问上界,并且n的范围还是1e18,所以考虑如何用o(1)或者o(logn)的时间复杂度构造出下界。
我们把下界如何确定并如何证明我们得到的数是下界分为以下几个步骤
1.首先,如果要想气最小,n个棋子一定是联通的
这个很好理解,联通一定比不连通气要少。
2.对于一个矩形,他的气其实和他的左边界与下边界构成L形状的联通块气是相同的
这一点也比较容易证明和理解,虽然这个L型区域和这个矩形的周长并不相同,但是有一些点是被共用的,经过平移可以发现他的气和原矩形一样气的总数都是原来矩形的周长
3.通过第二点可以推广出其实n个棋子的气一定大于等于这n个棋子放在平面上所占行数和列数的和的两倍
通过平移可以发现这一点,气一定大于等于平移后的矩形周长
4.所以我们能构成的矩形只有够铺满左边和下边,再往里依次放入新的棋子直到矩阵满之前都可以做到不增加气的个数
比如我们有一个5*5的矩阵,我们先用9个棋子铺满左边和下边,之后一次在L的拐角一个一个再放棋子,直到放到25个并不增加气的个数,也就是在这种矩形并且以这样构造方式下,9-25个棋子的气都满足<=2*(5+5)=20。通过这一点我们可以证明棋子下界是随着n单调不递减的,因为如果n个棋子可以做到气的总数为x,n-1个棋子一定可以通过这样的构造也做到气总数<=x的方法。
5.最后我们就可以将问题转化成多大的一个矩阵能容纳n个点(面积>=n),然后输出这个最小矩阵的周长
周长一定时显然越接近方阵面积越大(二次函数求导几个得到这一个结论),所以我们遍历能容纳面积大于等于n的最大边长,假设他为p,再判断一下p*(p-1)是否大于等于n即可。
6.不难发现遍历的时间复杂度是o(sqrt(n))的,显然是不能通过本题的,考虑直接使用sqrt函数找到根号n的大小再做比较即可。
比如计算出p=(long long)sqrt(n-0.5),这样p*p显然是小于n的,(p+1)*(p+1)显然是大于等于n的,还是判断一下(p+1)*p和n的大小关系即可。
7.但是其实做到6这一步是不能通过本题的。
比较有经验的人应该知道sqrt封装的是double型的不是longdouble型的,在n=1e18下double的精度显然会出问题,这一点大家可以自己本地尝试一下输出一下p的值即可,进一步就会发现不要说0.5的精度误差了,就连
输出出来的都是1e9,显然精度误差太大了。考虑如何用快速的方法对应边长,不难想到可以使用二分。然后就可以解决本问题了。
时间复杂度o(logn),空间复杂度o(1),代码如下:(其实比较难的是如何想到和给出证明,代码还是很简短的,构造题大多如此)
class Solution { public: /** * 往棋盘上放n个棋子,气的和的最小值 * @param n long长整型 * @return long长整型 */ long long minimum(long long n) { // write code here long long l=0,r=1e9+2,pos=-1; while(l<=r) { long long mid=(l+r)/2; if(mid*mid<n) { pos=mid; l=mid+1; } else r=mid-1; } if(pos*(pos+1)>=n) return 2*(pos*2+1); return 2*(pos*2+2); } };