简体   繁体   English

二进制搜索给出的结果略有错误

[英]Binary search given slightly inaccurate results

The problem I am trying to solve is the following: I get N rectangular paper strips with 1cm width and length C . 我要解决的问题如下:我得到N条矩形纸带,宽度为1cm,长度为C。 I need to cut the strips at a height where the sum of the areas of the cut strip is equal to A . 我需要切割条带的高度,其中所述切带的面积的总和等于A。 You can see an example bellow for which N = 5, the strips are of length, 5,3,6,2 and 3 cm and A = 3cm where the cut is made at 4cm. 您可以看到一个波纹管示例,其中N = 5,条带的长度分别为5,3,6,2和3厘米, A = 3厘米,其中的切口为4厘米。

URI在线判断

Note that I'm looking here for the red area. 请注意,我在这里寻找红色区域。

The input is given as follows. 输入如下。 The first line in each case begins with two integers N (1 ≤ N ≤ 10^5) and A (1 ≤ A ≤ 10^9) representing respectively the number of strips and the expected resulting area. 在每种情况下的第一行开始于两个整数N(1≤N≤10 ^ 5)和A(1≤A≤10 ^ 9)分别代表带的数量和预期的生成区域。 The next line contains N integers, representing the length C_i (1 <= C_i <= 10^4) of each strip. 下一行包含N个整数,代表每个条带的长度C_i(1 <= C_i <= 10 ^ 4)。 The input ends with A = C = 0, which should not be processed. 输入以A = C = 0结尾,不应对其进行处理。

For each test case, output a single line, the height H of the cut that must be done so that the sum of the area of the cut strips is equal to A cm². 对于每一个测试的情况下,输出一个单一的线,即必须进行切割的高度H,使得切割带的面积之和等于A平方厘米。 Print the answer with 4 decimal places. 打印答案的小数点后四位。 Output ":D" if no cutting is required, or "-.-" if it's impossible. 如果不需要切割,则输出“:D”;如果不可能,则输出“ -.-”。

This problem can be found here 这个问题可以在这里找到

My idea for solving this problem was to use a binary search where I pick a height in the middle of the strips and make it larger or smaller depending on whether my cut was too high or too low. 解决这个问题的想法是使用二进制搜索,我在条带中间选择一个高度,然后根据切割的高度是否太低将其增大或减小。 My implementation of the problem is given bellow: 问题的实现如下:

#include <iostream>
#include <vector>
#include <iomanip>
#include <algorithm>

using namespace std;

int main(){
    vector<int> v;  // Vector that holds paper heights
    int n;          // Number of papers
    double h,       // Height of the cut
           sum,     // Area sum
           min_n,   // Minimum height for cut to happen
           max_n,   // Maximum height for cut to happen
           a;       // Desired final area

    // Set desired output
    cout << fixed << setprecision(4);

    /* Get number of papers and desired area,
       terminates if N = A = 0
    */
    while(cin >> n >> a && (n||a)){
        v.resize(n); // Resize vector to fit all papers
        // Get all paper sizes
        for(int i=0;i<n;i++){
            cin >> v[i];
        }
        /* Sort the vector in decreasing order to
           simplify the search
        */
        sort(v.begin(),v.end(),greater<int>());
        max_n = v[0]; // Largest possible cut is at the height of the largest paper
        min_n = 0; // Smallest possible cut is at the base with height 0

        // Iterate until answer is found
        while(true){
            // Initialize cut height as the average of smallest and largest cut sizes
            h = (min_n + max_n)/2;

            /* The area sum is equal to the sum of the areas of each cut, which is
               given by the height of the paper minus the cut height. If the cut is
               higher than the paper, the cut has area 0.
            */
            sum = 0;
            // Using mascoj sugenstion, a few changes were added
            int s; // Temporary variable to hold number of time h is subtracted
            for(int i=0; i<n;i++){
                if(v[i] <= h) break; // From here onward cut area is 0 and there is no point adding
                sum += v[i]; // Removed the subtraction inside of the for loop
                s++; // Count how many paper strips were used
            }
            sum -= h*s // Subtracts the area cut from the s paper strips

            // If the error is smaller than the significant value, cut height is printed
            if(std::abs(sum-a) < 1e-5){
                // If no cut is needed print :D else print cut height
                (h < 1e-4 ? cout << ":D" << endl : cout << h << endl);
                break;
            }
            // If max_n is "equal" to min_n and no answer was found, there is no answer
            else if(max_n - min_n < 1e-7){
                cout << "-.-" << endl;
                break;
            }
            // Reduces search interval
            sum < a ? max_n = h : min_n = h;
        }
    }
    return 0;
}

The problem is, after submitting my answer I keep getting a 10% error. 问题是,提交我的答案后,我仍然收到10%的错误。 The website has a tool for comparing the output of you program with the expected output so I ran a test file with over 1000 randomly generated test cases and when I compared both I got a rounding error on the 4th decimal case, unfortunately, I don't have the file nor the script to generate test cases for me anymore. 该网站提供了一个将程序输出与预期输出进行比较的工具,因此我运行了一个包含1000多个随机生成的测试用例的测试文件,当我将两者进行比较时,我在四舍五入的小数点情况下均出现了舍入错误,但是,没有文件或脚本来为我生成测试案例。 I tried changing the acceptable error to a smaller one but that didn't work. 我尝试将可接受的错误更改为较小的错误,但这没有用。 I can't seem to find the error, does any of you have an idea of what is happening? 我似乎找不到错误,你们中的每个人都知道发生了什么吗?

ps: Although the problem doesn't say on the description, you can get cuts with fractions as heights ps:虽然问题没有在描述中说明,但您可以将分数作为高度进行切割

Might be your problem, maybe not: This line is exacerbating floating point error: sum += v[i]-h; 可能是您的问题,可能不是:这行加剧了浮点错误: sum += v[i]-h;

Floating points are only so accurate and compounding this error over a larger summation adds up. 浮点数是如此精确,并且在更大的总和上加总了这个误差。 I would try using multiplication on h and subtracting that from the total sum of applicable lengths. 我会尝试在h上使用乘法,然后从适用长度的总和中减去。 Should be well within the range of the double precision format so I wouldn't worry about overrunning the format. 应该在双精度格式的范围内,这样我就不必担心会超出格式范围。

Not sure to understand your algorithm but I think that can be done a lot simpler using a map instead a vector. 不确定要了解您的算法,但我认为可以使用地图而不是矢量来简化很多操作。

In the following example the map mp memorize how much (the value) strips are of a given lenght (the key). 在以下示例中,映射mp记忆给定长度(键)的带(值)条的数量。

An advantage of the map is ordered. 该地图的优势是有序的。

Next you can see how much you have to save (not to cat) and calculate the level of the cut starting from zero, adding 1 when appropriate and adding a fraction when neccessary. 接下来,您将看到必须保存(不算入)的数量,并计算从零开始的切割级别,适当时添加1,必要时添加分数。

Hope the following example can help 希望以下示例可以帮助您

#include <map>
#include <iomanip>
#include <iostream>

int main()
 {
   int                         n;
   int                         n2;
   int                         v;
   int                         cut;
   int                         a;
   std::map<int, std::size_t>  mp;
   long long int               sum;
   long long int               ts;


   std::cout << std::fixed << std::setprecision(4);

   while( (std::cin >> n >> a) && ( n || a ) )
    {
      mp.clear();

      n2  = 0;
      sum = 0LL;

      for ( auto i = 0 ; i < n ; ++i )
       {
         std::cin >> v;

         if ( v > 0 )
          {
            sum += v;

            ++mp[v];
            ++n2;
          }
       }

      // mp is a map, so the values are ordered

      // ts is "to save"; sum of lenghts minus a
      ts  = sum - a;

      // cut level
      cut = 0;

      // while we can add a full cm to the cut level
      while ( (ts > 0LL) && (n2 > 0) && (ts >= n2) )
       {
         ++cut;

         ts -= n2;

         if ( cut >= mp.cbegin()->first )
          {
            n2 -= mp.cbegin()->second;

            mp.erase(mp.cbegin());
          }
       }

      if ( (ts == 0LL) && (cut == 0) )
         std::cout << ":D" << std::endl; // no cut required (?)
      else if ( n2 == 0 )
         std::cout << "-.-" << std::endl; // impossible (?)
      else
         std::cout << (cut + double(ts) / n2) << std::endl;
    }
 }

ps: observe that a is defined as an integer in the page that you link. ps:请注意,您链接的页面中a被定义为整数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM