二进制搜索结果略有不准确

时间:2017-01-26 00:09:08

标签: c++ c++11 search binary-search

我想解决的问题如下:我得到 N 矩形纸条,宽度为1厘米,长度为 C 。我需要在切割条的面积总和等于 A 的高度处切割条带。您可以看到一个示例,其中 N = 5,条带长度为5,3,6,2和3厘米, A = 3厘米,其中切割为在4厘米处制作。

URI online judge

请注意,我正在寻找红色区域。

输入如下。每种情况下的第一行以两个整数开始 N (1≤ N ≤10^ 5)和 A (1≤ A ≤10^ 9)分别表示条带数量和预期结果区域。下一行包含N个整数,表示每个条带的长度C_i(1 <= C_i <= 10 ^ 4)。 输入以 A = C = 0结尾,不应处理。

对于每个测试用例,输出一条线,必须进行切割的高度 H ,以使切割条的面积总和等于 A cm²。打印4位小数的答案。如果不需要切割则输出“:D”,如果不可能则输出“-.-”。

可以找到此问题here

我解决这个问题的想法是使用二分搜索,我在条带的中间选择一个高度,并根据我的剪切是太高还是太低来使它变大或变小。我对问题的实施如下:

#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;
}

问题是,在提交我的答案后,我不断收到10%的错误。该网站有一个工具,用于比较你的程序输出和预期的输出,所以我运行了一个超过1000个随机生成的测试用例的测试文件,当我比较两个时,我在第4个十进制的情况下得到一个舍入错误,不幸的是,我不知道我有文件或脚本为我生成测试用例了。我尝试将可接受的错误更改为较小的错误,但这不起作用。我似乎无法找到错误,你们是否知道发生了什么?

ps:虽然问题没有在说明中说明,但您可以将分数作为高度进行削减

2 个答案:

答案 0 :(得分:0)

可能是你的问题,也许不是:这一行加剧了浮点错误:sum += v[i]-h;

浮点只是如此准确,并且在更大的总和上加起来这个错误加起来。我会尝试在h上使用乘法并从适用长度的总和中减去该乘法。应该在双精度格式的范围内,所以我不担心超出格式。

答案 1 :(得分:0)

不确定理解你的算法,但我认为使用地图而不是矢量可以做得更简单。

在以下示例中,地图Tab1Expenses会记住给定长度(关键字)的条带数量(值)。

地图的优势是有序的。

接下来你可以看到你需要保存多少(而不是cat)并计算从零开始的切割水平,在适当时加1并在必要时添加一个分数。

希望以下示例可以提供帮助

mp

p.s。:观察#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; } } 被定义为您链接的页面中的整数。