51Nod 2146 分割繩子 c/c++題解

題目描述

現在有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

題解:

我自己是沒想出來的,雖然知道是二分(想不出 邊界、二分判斷體的函數怎麼寫),還是看別人的看懂的。
思路

  1. 可以分割出的最小繩子長度爲0.0,最大繩子長度爲sumLen/k(sumLen:n條繩子的總長度,k:要分割的繩子數量),所以可以用二分去枚舉看哪個長度可以滿足條件儘可能的大,當然用O(n)的從後往前枚舉也行,就是速度慢多了。
  2. 什麼長度是滿足條件的呢?也就是怎麼寫check()函數,反正我的目的,就是在n條已知長度的繩子中割出k條長度爲mid的繩子,那麼就直接對每一條繩子直接切割即可:cnt += (int)(L[i]/len);,然後判斷cnt是不是>=k(大於k是因爲能夠分出更多當然也可以)。
  3. 判斷條件while(fabs(right-left) > Exp)是採用的這種操作,Exp是很小的值(自定義的)。
  4. 然後還有一點需要注意的就是輸出的精度問題:四捨五入保留兩位小數,可以用如下的方法:
    比如樣例的結果:2.005000(相對精確的值),
    然後:
  5. 2.005 * 1000 + 0.5 = 2005.5
  6. 2005.5 / 1000 = 2.0055
  7. 使用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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章