現在有N(1 <= N <= 1000)條繩子,
他們的長度分別爲L1,L2,……,Ln(1 <= Li <= 10000),如果從他們中切割出K(1 <= K <= 1000)條長度相同的繩子,
這K條繩子每條最長能多長?
輸入
共有兩行,第一行包含兩個正整數N和K,用一個空格分割;第二行包含N個數,一次表示N條繩子的長度,兩數間用一個空格分隔,每條繩子的長度的小數不超過兩位。
輸出
僅包含一個數,表示所得K條繩子的最大長度。答案四捨五入保留小數點後兩位
原題的樣例似乎出了個問題,樣例的精確的答案是2.005,四捨五入的話應該是2.01纔對。
輸入樣例
4 11
8.02 7.43 4.57 5.39
輸出樣例
2.01
題解:
我自己是沒想出來的,雖然知道是二分(想不出 邊界、二分判斷體的函數怎麼寫),還是看別人的看懂的。
思路:
- 可以分割出的最小繩子長度爲0.0,最大繩子長度爲sumLen/k(sumLen:n條繩子的總長度,k:要分割的繩子數量),所以可以用二分去枚舉看哪個長度可以滿足條件又儘可能的大,當然用O(n)的從後往前枚舉也行,就是速度慢多了。
- 什麼長度是滿足條件的呢?也就是怎麼寫check()函數,反正我的目的,就是在n條已知長度的繩子中割出k條長度爲mid的繩子,那麼就直接對每一條繩子直接切割即可:
cnt += (int)(L[i]/len);
,然後判斷cnt是不是>=k(大於k是因爲能夠分出更多當然也可以)。- 判斷條件
while(fabs(right-left) > Exp)
是採用的這種操作,Exp是很小的值(自定義的)。- 然後還有一點需要注意的就是輸出的精度問題:四捨五入保留兩位小數,可以用如下的方法:
比如樣例的結果:2.005000(相對精確的值),
然後:- 2.005 * 1000 + 0.5 = 2005.5
- 2005.5 / 1000 = 2.0055
- 使用printf("%.2"),即可輸出2.0055的四捨五入表示,得到2.01
代碼:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <deque>
#include <list>
#include <utility>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <bitset>
#include <iterator>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double E = exp(1.0);
const double Exp = 0.00000001;
const int MOD = 1e9+7;
const int MAX = 1e4+5;
int n;
double L[MAX];
int k;
// 二分的check還沒寫
bool check(double len)// 給出一個長度,判斷 是否可以從n條繩子中切割出k條長度爲len的繩子
{
int cnt = 0;
for(int i = 0; i < n; i++)
{
cnt += (int)(L[i]/len);
}
if(cnt >= k)
{
return true;
}
else
{
return false;
}
}
int main()
{
/*
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
*/
while(cin >> n >> k)
{
double sumLen = 0;
for(int i = 0; i < n; i++)
{
cin >> L[i]; sumLen += L[i];
}
/*
從n條繩子裏面,切割出k條長度相等的繩子,這k條繩子最長能多長。
*/
double left = 0.0;// 最小分割長度
double right = sumLen/k;// 最大分割長度
double mid;
while(fabs(right-left) > Exp)
{
mid = (left+right)/2;
if(check(mid))
{
// 判斷把mid作爲長度是否滿足要求
// 要求:能夠劃分出k條長度相同的繩子
left = mid;
}
else
{
right = mid;
}
}
// 保留2位小數
double tmp = mid*1000 + 0.5;// 2.005
tmp /= 1000;
printf("%.2f\n",tmp);
}
return 0;
}