简体   繁体   中英

Just doing some exercises and ran into some problems, don't know if its logic or what. I need some eyes

I always come back to this problem, I've never solved it. I don't know why it gives me so many problems. I usually can get passed the first four test cases fine, then it fails or runs too slow. Its been almost a year since I took a crack at it, so I decided to give it a fresh look. I'm not in school, this is just for fun, or at least I thought it would be. I took a geometrical approach, and used a recursive method. I'd like to know if anyone can pin point where my logic goes wrong, a test case that would fail the logic. Every test case I've come up with on my own seems to be correct when I calculate manually. Unless my math is wrong; I really hope that is not the case.

So this is the problem: https://open.kattis.com/problems/a1paper

I'm given 2 lines of input, the first one tells me the lowest number paper size, A2,3,4,5,.... the next line gives me the amount of paper I have starting at size 2, 3, 4, n.

The idea is to determine the amount of tape needed to tape the papers together to get an A1 paper.

this is my solution:

import java.util.Scanner;

public class A1Paper
{   
    private static Scanner scan = new Scanner(System.in);
    private static final double ABSOLUTE_ERROR = Math.pow(10.0, -5.0);

    public static void main(String[] args)
    {
        final double SHORT_SIDE = Math.pow(2.0, -5.0/4.0);
        final double LONG_SIDE = Math.pow(2.0, -3.0/4.0);
        final double A1_AREA = 2 * SHORT_SIDE * LONG_SIDE;

        int remainingSizes = scan.nextInt() - 1;
        scan.nextLine();

        Result result = process(remainingSizes, SHORT_SIDE, LONG_SIDE, A1_AREA);

        if (result != null)
            System.out.println(result.getTape());
        else
            System.out.println("impossible");

        scan.close();
        return;
    }

    private static Result process(int remainingSizes, double shortSide, double longSide, double remainingArea)
    {   
        if (withinAbsoluteError(remainingArea))
        {
            return null;
        }

        if (remainingSizes == 0)
            return null;

        int remainingSheets = scan.nextInt();

        int sheetsUsed = 0;

        if (remainingSheets > 0)
        {
            double sheetArea = shortSide * longSide;

            while (remainingSheets > 0 && !withinAbsoluteError(remainingArea))
            {   
                remainingSheets--;
                sheetsUsed++;
                remainingArea -= sheetArea;
            }
        }

        Result prevResult = process(remainingSizes - 1, longSide/2.0, shortSide, remainingArea);
        Result thisResult = null;

        if (prevResult == null)
        {
            if (sheetsUsed%2 != 0 || sheetsUsed == 0)
                return null;

            thisResult = new Result(sheetsUsed/2, sheetsUsed/2 * longSide);
        }
        else
        {
            sheetsUsed += prevResult.getSheets();

            if (sheetsUsed%2 !=0)
                return null;

            thisResult = new Result(sheetsUsed/2, prevResult.getTape() + (sheetsUsed/2 * longSide));
        }

        return thisResult;
    }

    private static boolean withinAbsoluteError(double remainingArea)
    {   
        if (remainingArea <= ABSOLUTE_ERROR && remainingArea >= -ABSOLUTE_ERROR)
            return true;
        else
            return false;
    }

    private static class Result
    {
        private int sheets;
        private double tape;

        public Result(int sheets, double tape)
        {
            this.sheets = sheets;
            this.tape = tape;
        }

        public int getSheets()
        {
            return sheets;
        }

        public double getTape()
        {
            return tape;
        }
    }
}

Apparently there exists a testcase this will not work for, Kattis didn't want to give me the test cases, so I'm just curious if anyone has any idea. As I stated, every example I've come up with seems to pass.

I'll explain my function here:

private static Result process(int remainingSizes, double shortSide, double longSide, double remainingArea)

The first call to the function:

remainingSizes : the first int received from the input stream. Because it gives you the smallest sized paper, eg. 4 (A4) , then 3 would be the remaining sizes (A2, A3, A4).

shortSide and longSide for size A2 is given and used to call the function on its first iteration:

final double SHORT_SIDE = Math.pow(2.0, -5.0/4.0);
final double LONG_SIDE = Math.pow(2.0, -3.0/4.0);

remainingArea: when originally passed on the first function call, the Area needed to achieve A1, or double the area of A2.

The two cases that end recursion, either the area is within the absoluteError, or we run out of different sized paper:

if (withinAbsoluteError(remainingArea))
            {
                return null;
            }

if (remainingSizes == 0)
                return null;

The next part of the function takes in the amount of A2 sheets (in the first call). If we have sheets, then calculate the area of A2, and subtract from the remaining area while keeping count of the sheets used and remaining sheets. Then comes the recursive call to the method, cutting the paper in half to obtain the next sized sheet, eg A3.

int remainingSheets = scan.nextInt();

        int sheetsUsed = 0;

        if (remainingSheets > 0)
        {
            double sheetArea = shortSide * longSide;

            while (remainingSheets > 0 && !withinAbsoluteError(remainingArea))
            {   
                remainingSheets--;
                sheetsUsed++;
                remainingArea -= sheetArea;
            }
        }

        Result prevResult = process(remainingSizes - 1, longSide/2.0, shortSide, remainingArea);

This process will continue until I've met the area or run out of sheets.

In the next part of the method is where I determine the amount of tape used:

Result thisResult = null;

        if (prevResult == null)
        {
            if (sheetsUsed%2 != 0 || sheetsUsed == 0)
                return null;

            thisResult = new Result(sheetsUsed/2, sheetsUsed/2 * longSide);
        }
        else
        {
            sheetsUsed += prevResult.getSheets();

            if (sheetsUsed%2 !=0)
                return null;

            thisResult = new Result(sheetsUsed/2, prevResult.getTape() + (sheetsUsed/2 * longSide));
        }

        return thisResult;'

theres two possibilities here, either the previous call returned null, and there were no pages, or there were pages. The way I looked at it was, if I'm working back through the function calls, there has to be an even amount of pages as I tape them together and make larger pages.

eg:

If i had 1 - A2, 1 - A3, 2 - A4

then taping the 2 - A4 along their long side, would make an A3, add that to the A3 i have and thats 2 - A3. tape those 2-A3 together makes an A2, add that to the one i have, thats 2-A2. And ofcoarse, two A2 makes an A1.

I dont know if this explination cleared up my code at all, sorry if I made it more confusing than it had to be.

As pointed out by https://stackoverflow.com/users/1899640/that-other-guy , I was not using the tolerance provided in the question correctly.

The tolerance given is for the final answer. If you have to sum 0.00001 a million times, the answer needs to be between 9.99999 and 10.00001. Your code instead says it's ok if the answer is 0, because 0.00001 is within the tolerance so none of the million pieces need to be counted. This is not sound reasoning. – that other guy

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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