c++ - 从火柴棒制作数字的算法

标签 c++ algorithm recursion puzzle

我编写了一个程序来解决this problem来自 ACM。

Matchsticks are ideal tools to represent numbers. A common way to represent the ten decimal digits with matchsticks is the following:

This is identical to how numbers are displayed on an ordinary alarm clock. With a given number of matchsticks you can generate a wide range of numbers. We are wondering what the smallest and largest numbers are that can be created by using all your matchsticks.

Input

On the first line one positive number: the number of testcases, at most 100. After that per testcase:

One line with an integer n (2 ≤ n ≤ 100): the number of matchsticks you have. Output

Per testcase:

One line with the smallest and largest numbers you can create, separated by a single space. Both numbers should be positive and contain no leading zeroes. Sample Input

4 3 6 7 15 Sample Output

7 7 6 111 8 711 108 7111111

问题是解决 100 根火柴棒的速度太慢了。搜索树太大,无法对其进行暴力破解。

前 10 个结果如下:

2: 1 1

3: 7 7

4:4 11

5: 2 71

6: 6 111

7: 8 711

8: 10 1111

9: 18 7111

10: 22 11111

最大值的模式很简单,但我没有找到最小值的捷径。有人可以建议更好的方法来解决这个问题吗?这是我使用的代码:

    #include <iostream>
    #include <string>
    using namespace std;

    #define MAX 20 //should be 100

    //match[i] contains number of matches needed to form i
    int match[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
    string mi[MAX+1], ma[MAX+1];
    char curr[MAX+1] = "";

    //compare numbers saved as strings
    int mycmp(string s1, string s2)
    {
        int n = (int)s1.length();
        int m = (int)s2.length();
        if (n != m)
            return n - m;
        else
            return s1.compare(s2);
    }

    //i is the current digit, used are the number of matchsticks so far
    void fill(int i, int used)
    {
        //check for smaller and bigger values
        if (mycmp(curr, mi[used]) < 0) mi[used] = curr;
        if (mycmp(curr, ma[used]) > 0) ma[used] = curr;

        //recurse further, don't start numbers with a zero
        for (int a = i ? '0' : '1'; a <= '9'; a++) {
            int next = used + match[a-'0'];
            if (next <= MAX) {
                curr[i] = a;
                curr[i+1] = '\0';
                fill(i + 1, next);
            }
        }
    }

    int main()
    {
        //initialise 
        for (int i = 0; i <= MAX; i++) {
            mi[i] = string(MAX, '9');
            ma[i] = "0";
        }

        //precalculate the values
        fill(0, 0);

        int n;
        cin >> n;

        //print those that were asked
        while (n--) {
            int num;
            cin >> num;
            cout << mi[num] << " " << ma[num] << endl;
        }

        return 0;
    }

编辑:我最终使用了动态规划解决方案。我之前用 dp 试过,但我在摆弄一个二维状态数组。这里的解决方案要好得多。谢谢!

最佳答案

您可以使用动态规划解决方案。

假设你有 n 个匹配项,并且你知道如何解决所有 n-k 匹配项的问题(最小数量),其中 k 取对应的所有值到每个数字使用的匹配数(2 代表 1,5 代表 3,等等)

然后递归地导出解决方案。假设您的数字以 1 结尾(在最低位),那么最好的解决方案是编写 (best solution with n-2 matches) 1。假设你以 2 结尾,最好的解决方案是 (best solution with n-5 matches) 2。等等 ;最终你可以比较这十个数字,然后选出最好的一个。

所以现在,您所要做的就是递归地为所有小于您的输入的 n 设计最佳解决方案。

编辑:如果您以直接的方式实现此算法,您最终会得到指数级的复杂度。这里的技巧是要注意,如果您的核心函数 MinimumGivenNMatches 只接受一个参数,n。因此,您最终会多次使用相同的值调用它。

要使复杂度呈线性,您只需使用辅助数组记住(即记住)每个 n 的解决方案。

关于c++ - 从火柴棒制作数字的算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6776304/

相关文章:

c++ - 如何从一个文件中读取数据并将其导入到另一个文件中?

sql - 计算路段成本

algorithm - 如何在 “ascending” 三次贝塞尔曲线中以 Δx 的间隔有效地采样 y?

c++ - 静态变量在递归中的行为

c++ - 在 TravisCI 下为 cmake 更改 C++ 编译器

c++ - 用下划线填充空白

c++ - 使用 shared_ptr 的开销和实现

algorithm - 何时使用排序算法

R 中的递归 %in% 函数?

c++ - 如何在递归函数调用中返回当前函数值