如何在此方案中找到最佳策略?

时间:2017-03-05 03:28:27

标签: algorithm mathematical-optimization

场景看起来像这样: 给定一个1和2的数组,我们想要做到全部。有以下限制:

  1. 在每一步中,其中一个可以与相邻的一个交换位置。

             1 2 1 2 1
             could be transformed to:
             2 1 1 2 1 
             in single step.
    
  2. 如果边缘出现2,则可以一步将其分解为2个。

             2 1 2 1
           1 1 1 2 1
           in single step
    
  3. 如果两个两个相邻,它们可以被分解成一个。

            1 2 2 1
            into
            1 *1 1 1 1* 1
            it costs us 2 steps.
    
  4. 所以问题是,给定n个步骤我们可以做到全部吗? 我不希望得到一些小见解的完整答案。

2 个答案:

答案 0 :(得分:2)

这是一个建设性的解决方案。

引理:在边界附近(左右两侧)打破多个2永远不是最佳选择。

证明:假设在最佳解决方案中,我们打破了左边界附近最左边的两个2,他们在阵列中的位置是xyx <= y)。我们使用x + y + 2操作来制动它们。但是如果我们只是在相邻的tile中移动它们并执行第三种类型的操作,我们只实现y - x + 1次操作。所以以前的解决方案不是最优的证实。

所以只有4个案例:

  • 根本不要使用第二次操作(仅当2的数量为偶数时)。
  • 仅在左边框处使用第二个操作(仅当2的编号为奇数时)。
  • 仅在右边框使用第二个操作(仅当2的编号为奇数时)。
  • 在两个边界使用第二个操作(仅当2的数量为偶数时)。

在上层过程之后,我们有偶数2。所以只需将它们配对并通过第3次操作制动。

解决方案的复杂性为O(n),因为我们有O(n)输入数据,这是处理此问题的最佳方式。

随意提问或代码。

答案 1 :(得分:0)

我尝试开发动态编程算法,但找不到最佳子结构

这是暴力最耗时的递归解决方案

我们必须找出是否可以减少对所有1的输入。 输入数组有1和2,并且有规则将2转换为1。

天真的方法是尽可能地应用规则并尝试找出是否可以实现输出

伪代码算法是:

scan caching = 100
record size = ~5kb
cf block size = ~130kb, compression=gz

所有函数的不那么elegnet java实现:

boolean check(int[] input, int steps) {
    if(steps == 0) {
        return allOnes(input)
    }
    step--
    boolean ans = false;

    if(input[0] == 2) {
        breakEdgeTwoIntoOne(input,0) // function for rule 2
        revert(input) // convert input to original 
        ans = ans | check(input,steps)
    }

    if(input[input.length-1] == 2) {
        breakEdgeTwoIntoOne(input,input.length - 1)
        revert(input)
        ans = ans | check(input,steps)
    }

    for(i=0;i<input.length-1;i++) {
        if(input[i] == 2 && input[i+1] == 2) {
            breakTowAdjecentTwosIntoOnes(input,i) // function for rule 3
            revert(input)
            ans = ans | check(input,steps)
        }
    }

    for(i=0;i<input.length-1;i++) {
        if( input[i] == 2 ) {
            swapLeft(input, i) // function for rule 1
            ans = ans | check(input,steps)
            revert(input)
            swapRight(input, i)
            ans = ans | check(input,steps)
            revert(input)
        }
    }

    return ans;
}

一些测试用例

static boolean check(int[] input, int steps) {
    if (allOnes(input)) {
        return true;
    }
    if (steps == 0) {
        return allOnes(input);
    }
    steps--;
    int original[] = new int[input.length];
    System.arraycopy(input, 0, original, 0, input.length);
    boolean ans = false;
    if (input[0] == 2) {
        ans = ans | check(breakEdgeTwoIntoOne(input, 0), steps);
        System.arraycopy(original, 0, input, 0, input.length);
    }

    if (input[input.length - 1] == 2) {
        ans = ans | check(breakEdgeTwoIntoOne(input, input.length - 1), steps);
        System.arraycopy(original, 0, input, 0, input.length);
    }

    for (int i = 0; i < input.length - 1; i++) {
        if (input[i] == 2 && input[i + 1] == 2) {
            ans = ans | check(breakTowAdjecentTwosIntoOnes(input, i), steps);
            System.arraycopy(original, 0, input, 0, input.length);
        }
    }
    for (int i = 0; i < input.length - 1; i++) {
        if (input[i] == 2) {
            if (i != 0) {
                ans = ans | check(swapTwoToLeft(input, i), steps);
                System.arraycopy(original, 0, input, 0, input.length);
            }
            if (i != input.length - 1) {
                ans = ans | check(swapTwoToRight(input, i), steps);
                System.arraycopy(original, 0, input, 0, input.length);
            }
        }
    }
    return ans;
}

private static int[] breakTowAdjecentTwosIntoOnes(int[] input, int i) {
    int op[] = new int[input.length + 2];
    int k = 0;
    for (int j = 0; j < input.length; j++) {
        if (j == i || j == i + 1) {
            op[k++] = 1;
            op[k++] = 1;
            // j++;
        } else {
            op[k++] = input[j];
        }
    }
    return op;
}

private static int[] breakEdgeTwoIntoOne(int[] input, int i) {
    int op[] = new int[input.length + 1];
    if (i == 0) {
        op[0] = 1;
        op[1] = 1;
        System.arraycopy(input, 1, op, 2, input.length - 1);
    } else {
        op[op.length - 2] = 1;
        op[op.length - 1] = 1;
        System.arraycopy(input, 0, op, 0, input.length - 1);
    }
    return op;

}

private static int[] swapTwoToRight(int[] input, int i) {
    if (i != input.length - 1) {
        int k = input[i + 1];
        input[i + 1] = input[i];
        input[i] = k;
    }
    return input;
}

private static int[] swapTwoToLeft(int[] input, int i) {
    if (i != 0) {
        int k = input[i - 1];
        input[i - 1] = input[i];
        input[i] = k;
    }
    return input;
}

private static boolean allOnes(int[] input) {
    for (int x : input) {
        if (x != 1)
            return false;
    }
    return true;
}