场景看起来像这样: 给定一个1和2的数组,我们想要做到全部。有以下限制:
在每一步中,其中一个可以与相邻的一个交换位置。
1 2 1 2 1
could be transformed to:
2 1 1 2 1
in single step.
如果边缘出现2,则可以一步将其分解为2个。
2 1 2 1
1 1 1 2 1
in single step
如果两个两个相邻,它们可以被分解成一个。
1 2 2 1
into
1 *1 1 1 1* 1
it costs us 2 steps.
所以问题是,给定n个步骤我们可以做到全部吗? 我不希望得到一些小见解的完整答案。
答案 0 :(得分:2)
这是一个建设性的解决方案。
引理:在边界附近(左右两侧)打破多个2
永远不是最佳选择。
证明:假设在最佳解决方案中,我们打破了左边界附近最左边的两个2
,他们在阵列中的位置是x
和y
(x <= 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;
}