题目
题目描述:
二月中旬虐狗节前夕,华华决定给月月准备一份礼物。为了搭建礼物的底座,华华需要若干根同样长的木棍。华华手头上有一些长度参差不齐的木棍,他想将每根都裁剪成若干段自己想要的长度,并丢掉多余的部分。因为华华的手很巧,所以他的裁剪过程不会有任何的失误。也就是说,对于一根长度为N的木棍,华华可以精准的将它们裁剪为若干段木棍,使它们的长度之和为N。
华华不知道裁剪成多长比较好,所以干脆越长越好。不过由于华华有点强迫症,所以他希望长度为非负整数。保证所有木棍的原长也是非负整数。那么请问华华最终得到的每根木棍多长呢?
输入描述:
第一行两个正整数N、K,表示木棍原本的根数和华华希望得到的木棍根数。
第二行N个正整数Li表示每根木棍的初始长度。
输出描述:
输出一行一个非负整数表示每根木棍的最大长度。
解析
首先题目这么长,我们来看一遍题目
- 看过一遍以后这道题的意思其实就是:N根木棍裁剪后长短相同,最长是多少。
那么问题就来了:
- 木棍不可能裁的比自己还长。
- 木棍在裁剪过程中很可能会有浪费,而且浪费木材不能使用。
所以现在我们可以构建基本思路:
- 将数据存入一个数组使用
- 用二分法(下面列专栏讲)查找长度的范围,知道找到答案(因为是非负整数,所以一定可以找到答案)
只有两条?对,思路就是这么简单,但我最开始不会orz,哭唧唧
憨憨还试着从一个较大值一个一个试下来。在大佬的思路提示下才理解
二分
关于这道题,我就有所不知。
我们常用的二分法:做查找类运算,利用缩减范围而达到查找到所需的位置。
惯性思维让我对于二分只想到用在查找数据上(其实我都快忘了二分了)
然而这道题还真是个查找题。。。
拿这道题举例:
- 我们已知需求的木棍范围在0~1e9
- 对该区间进行二分查找,在区间0~1e9中找到最长的那个K
- 每次对中点mid(K的假定值)进行操作,判断最长K是在左区域还是右区域,以此减少一半的范围
- 以此类推,直到相等
这是二分的基本操作:
while (left < right) { int mid = left + right>> 1; int key = 0; /**************************** 在这里进行对key的基本操作 ****************************/ if (mid >= key) left = mid;//此处mid不能+1的原因是:判断包括了mid==key的情况 else right = mid - 1; //此处视key的情况判断左右区间 }
AC代码
#include <iostream> #include <vector> using namespace std; typedef long long ll; #define js ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); const int MAX = 1e9 + 9; int main() { js; ll N, K; cin >> N >> K; vector<ll> L; for (ll i = 0; i < N; i++) { ll t; cin >> t; L.push_back(t); } ll left = 0, right = MAX;//左右范围 while (left < right) { ll sum = 0, mid = left + right + 1 >> 1; //+1是为了让分母不为0,mid只是一个中间值,改变一点影响不大 for (ll i = 0; i < N; i++) sum += L[i] / mid; //sum求出在mid值下最大可以得到的木条数 if (sum >= K) left = mid; else right = mid - 1; //可以得到的多于你要的数量时,表示mid短了(短了才会更多)。反之亦然 } cout << left << endl; return 0; }