【问题标题】:Can't figure out what the generic name of this algorithm is called?不知道这个算法的通用名称叫什么?
【发布时间】:2015-05-07 14:42:18
【问题描述】:

我刚刚做了一个 Top Coder SRM,其中有一个问题我需要解决。我正在尝试在线搜索算法的详细信息,但似乎找不到。

这个问题围绕着以下几行展开: 你有一个数组,例如 [12, 10, 4]

每一轮,您可以应用 9,3,1 的任意排列从 [12,10,4] 中减去

返回为使数组中的所有数字变为 0 所需的最小排列量。

有什么帮助吗?

编辑:让我描述得更详细一些,以便更好地理解问题。

One question would be
input: [12 10 4]
output: 2 (minimum rounds)

How it would work:
[12 10 4] - [9 3 1] = [3 7 3]
[12 10 4] - [9 1 3] = [3 9 1]
[12 10 4] - [3 9 1] = ..
[12 10 4] - [3 1 9] = ..
[12 10 4] - [1 3 9] =
[12 10 4] - [1 9 3] =

[3 7 3] - [3 9 1] = ...

..
[9 1 3] - [9 1 3] = [0 0 0] <-- achieved with only two permutation subtractions

编辑2:

这是 topcoder 的问题: http://community.topcoder.com/stat?c=problem_statement&pm=13782

编辑3: 如果他们认为解决方案是动态规划,任何人都可以解释重叠子问题和最优子结构吗?

【问题讨论】:

  • 是的,在纸上计算出来之后(显然我不能在我的脑海中这样做!),你的意思是像 [12, 10, 4] - [3, 9, 1] = [9, 1, 3] - [9, 1, 3] = [0, 0, 0]?
  • “最小排列量”是什么意思?对于你的例子,答案是什么?
  • 我认为没有专门解决这个问题的经典算法。不过有趣的问题是,您的数组的最大长度是多少?
  • 数组的最大大小是多少?

标签: algorithm recursion dynamic combinations permutation


【解决方案1】:

编辑:

在看到原始问题后,这里是为上述问题链接中提供的每个测试用例提供正确输出的解决方案,如果你想输入 {60} ,你应该给出 {60,0,0} 。

基本思想是,你需要让它们都小于或等于 0 atlast ,所以如果数字能被 3 整除,你可以通过减去 9 或 3 得到零,如果不能被整除,则减去 1能被 3 整除

首先,对给定的三元组进行排序,然后

  • 检查最大的数能被3整除,如果是,减去9,这个数仍然能被3整除
  • 现在检查下一个最大数,如果能被 3 整除,则减 3,否则减 1
  • 如果最大数不能被 3 整除,则减 1 使其可以被 3 整除
  • 现在将下一个最大数减去 9,另一个减去 3

这里是实现

#include <iostream>
#include <algorithm>
#include<cmath>
using namespace std;
int f(int* a,int k){
sort(a,a+3);
if(a[2]<=0 )
{cout << k<< endl ;
return 0;}

if(a[1]<=0){
cout << k+ceil((double)a[2]/9) << endl;
return 0;

}
if(a[2]%3==0 ){
a[2]=a[2]-9;
if(a[1]%3==0 )
{
    a[1] = a[1] -3;
    a[0] = a[0] -1;
}
else{
    if(a[2]%2==0 && (a[1]-1)%2==0  && a[1]!=0){
        a[2]=a[2] +9 -3;
        a[1] = a[1] -9;
        a[0] = a[0]-1;
    }
    else{
    a[1] = a[1] -1;
    a[0] = a[0] - 3;}
}
return f(a,++k);
}
else{
a[2] = a[2] -1;

    a[1] = a[1] -9;
    a[0]=a[0] -3;
return f(a,++k);

}
}
int main() {

int a[] = {54,18,6};
f(a,0);
return 0;
}

希望对你有帮助

示例: 输入是[12,4,10]

排序[12,4,10] -&gt; [12,10,4]

现在检查最大数是否可以被3整除,这里是Yes

所以 [12-9,10,4] -&gt; [3,10,4] 现在检查下一个能被3整除的最大数,这里是N0

所以 [3,10-1,4-3] -&gt;[3,9,1]

现在增加计数并将其传递给函数 (recursive)

输入 -> [3,9,1]

排序[3,9,1] -&gt; [9,3,1]

现在检查最大数是否可以被3整除,这里是Yes

所以[9-9,3,1] -&gt; [0,3,1]

现在检查下一个能被3整除的最大数,这里是Yes

所以[0,3-3,1-1] -&gt; [0,0,0]

现在增加计数并传递数组

由于最大元素是 0 ,我们将打印计数,即 2

【讨论】:

  • 那我不明白你的算法。给出一个给定初始数组[12,10,4] 的例子。显示每个步骤。
  • @JimMischel , 10 不能被 3 整除,所以我减去 1 ,所以它变成 [12,10,4] - [9,1,3] = [3,9,1] ,请考虑查看代码,我运行代码并得到输出 2
  • 嗯……有趣的算法。你能证明它在所有情况下都有效吗?
  • @JimMischel,如果结果可以是 [0,0,0] 那么它会工作先生,否则我需要扩展它先生,但根据问题,这已经足够了先生
  • 我不太确定。给定[15,13,11],您的算法减去[9,1,3] 得到[6,12,8]。排序到[12,8,6] 并减去(同样,通过你的算法)[9,1,3],你最终得到[3,7,3],这是一个死胡同。但是如果你从[15,13,11]开始,减去[3,9,1]得到[12,4,10],那么它是可以解决的。
【解决方案2】:

设元素个数为n,设p1 到pn! 为要减去的数组的排列,设A 为你的原始数组。然后你想要线性系统的自然数解

A - k1*p1 - ... - kn!*pn! = 0

相当于

A = k1*p1 + ... + kn!*pn!

其中0 是全零的n-item 数组。由于ℕ^n 不是-向量空间,因此您无法使用明显的线性代数解决方案来解决这个问题;实际上,通常在自然数上找到线性系统的解决方案是NP-complete by a reduction from subset sum。我无法根据您对问题的版本进行临时调整,因此请考虑一下。它应该保持 NP-hard,至少这是我所期望的。

【讨论】:

    【解决方案3】:

    可以使用动态规划来解决。如果您查看动态规划问题的常见解决方案,它们遵循相同的一般结构。我们使用一个数组来存储达到每个值所需的最小排列数,并使用嵌套循环依次更新每个值。答案将以d[0][0][0] 结尾,这是到达 [0, 0, 0] 所需的排列数。

    public static int count(int a, int b, int c) {
        int[][] permutations = {{9, 3, 1}, {9, 1, 3}, {1, 9, 3}, {1, 3, 9}, {3, 9, 1}, {3, 1, 9}};
    
        int[][][] d = new int[a + 1][b + 1][c + 1];
    
        // Set initial values to high value to represent no solution.
        for(int x = 0; x <= a; x++) {
            for(int y = 0; y <= b; y++) {
                for(int z = 0; z <= c; z++) {
                    d[x][y][z] = Integer.MAX_VALUE / 2;
                }
            }
        }
    
        // Set number of permutations for initial value to 0.
        d[a][b][c] = 0;
    
        // Update all values.
        for(int x = a; x >= 0; x--) {
            for(int y = b; y >= 0; y--) {
                for(int z = c; z >= 0; z--) {
                    for(int[] p:permutations) {
                        // Update count from [x, y, z] -> [nx, ny, nz] using permutation p.
                        int nx = x - p[0];
                        int ny = y - p[1];
                        int nz = z - p[2];
                        if(nx >= 0 && ny >= 0 && nz >= 0) {
                            d[nx][ny][nz] = Math.min(d[nx][ny][nz], d[x][y][z] + 1);
                        }
                    }
                }
            }
        }
    
        // Return final answer.
        return d[0][0][0];
    }
    

    【讨论】:

    • 看问题陈述,有些值可以小于0,数组中值的个数可以变化。相应地调整解决方案应该很容易。
    • 嘿 fgb,如果这可以用动态规划解决,你能解释一下重叠子问题和最优子结构是什么吗?我知道这两者都是动态编程问题的属性,但我不清楚它们是什么?
    • @McFlurriez 它有重叠的子问题,因为减去 [9, 3, 1] 然后 [9, 1, 3] 与减去 [9, 1, 3] 然后 [9, 3, 1] 相同]。无论哪种方式,您都会遇到相同的子问题。它具有最佳子结构,因为如果您的值具有n 最小排列,那么减去其中一个排列将为您提供具有n-1 最小排列的值。问题的一个实例可以简化为同一问题的更简单版本。
    猜你喜欢
    • 2013-04-01
    • 2020-06-21
    • 2015-09-17
    • 1970-01-01
    • 2013-01-24
    • 2012-01-18
    • 2014-08-28
    • 1970-01-01
    相关资源
    最近更新 更多