切成矩形的最小切角

时间:2019-06-22 17:30:07

标签: c++ algorithm

我正在尝试解决此问题:

  

给定一个a×b矩形,您的任务是将其切成正方形。每次移动时,您都可以选择一个矩形并将其切成两个矩形,以使所有边长均保持整数。最少可以移动多少次?

我的逻辑是,最少的切割数意味着最少的平方数;我不知道这是正确的方法。

我看到哪一侧较小,现在我知道我需要切割削减的bigSide / SmallSide以具有smallSide边的正方形,然后剩下SmallSide和bigSide%smallSide。然后我继续直到任一边都为0或两者相等。

#include <iostream>

int main() {
    int a, b; std::cin >> a >> b; // sides of the rectangle

    int res = 0;

    while (a != 0 && b != 0) {
        if (a > b) {
            if (a % b == 0)
                res += a / b - 1;
            else
                res += a / b;
            a = a % b;
        } else if (b > a) {
            if (b % a == 0)
                res += b / a - 1;
            else
                res += b / a;
            b = b % a;
        } else {
            break;
        }
    }

    std::cout << res;

    return 0;
}

当输入为404 288时,我的代码给出了18,但正确的答案实际上是10

我在做什么错了?

1 个答案:

答案 0 :(得分:4)

在我看来,问题很明显地定义了每个动作,即沿着整数线将一个矩形切割成两个矩形,然后要求这种切割的最小数量。如您所见,此问题具有明显的递归性质。将矩形切成两部分后,可以递归并以最少的移动将它们切成正方形,然后对答案求和。问题在于递归可能导致指数时间复杂度,这直接导致我们直接dynamic programming。您必须使用memoization才能有效地解决它(最坏的情况是ngOnChanges() { setTimeout(() => this.input.nativeElement.select()); } ),这是我建议做的:

O(a*b*(a+b))

foor循环上升到#include <iostream> #include <vector> using std::vector; int min_cuts(int a, int b, vector<vector<int> > &mem) { int min = mem[a][b]; // if already computed, just return the value if (min > 0) return min; // if one side is divisible by the other, // store min-cuts in 'min' if (a%b==0) min= a/b-1; else if (b%a==0) min= b/a -1; // if there's no obvious solution, recurse else { // recurse on hight for (int i=1; i<a/2; i++) { int m = min_cuts(i,b, mem); int n = min_cuts(a-i, b, mem); if (min<0 or m+n+1<min) min = m + n + 1; } // recurse on width for (int j=1; j<b/2; j++) { int m = min_cuts(a,j, mem); int n = min_cuts(a, b-j, mem); if (min<0 or m+n+1<min) min = m + n + 1; } } mem[a][b] = min; return min; } int main() { int a, b; std::cin >> a >> b; // sides of the rectangle // -1 means the problem is not solved yet, vector<vector<int> > mem(a+1, vector<int>(b+1, -1)); int res = min_cuts(a,b,mem); std::cout << res << std::endl; return 0; } a/2的原因是裁切纸是对称的:如果沿垂直线b/2裁切,则与沿线裁切相同i(如果您垂直翻转纸张)。这是一个小小的优化技巧,可将复杂度总体降低a-i。 另一个小技巧是,知道问题是如果对纸张进行转置,结果是相同的,只需4,就可以将计算量减少一半。但是任何进一步的重大改进,例如贪婪算法,都将需要更多的思考(如果有的话)。