使用给定操作对数组进行排序

时间:2015-04-03 15:58:16

标签: algorithm dynamic-programming

给出标记为1到M的M个数字的列表。此外,我们给出了类型为:X Y

的Q运算

每个操作意味着我们可以简单地将值X的值更改为值为Y的数字或反之亦然。这些操作也是可传递的。这意味着如果X-> Y和Y-> Z,则X也可以变为Z.

现在我们给出了一个N个数字的序列,我们需要通过修改最小数量来使其按升序排序(可能不会严格增加)。如果不可能,则通过返回-1来告诉它不可能。

示例:假设我们有3个数字(= M)和3个操作(= Q),如下所示:

1 2
2 3
1 3

并且让N = 3,将数组修改为[3,2,1],然后回答为2(因为我们修改数组中的2个数字),因为可以将第一个数字转换为1,将最后一个数字转换为2得到的数组是[1,2,2],它正在增加。

但是让M = 3并且我们只有一次操作:2 3。同样让N = 2,数组为[2,1]则不可能对其进行排序,因为它只能转换为[2,1]或[3,1]。

那么如何解决这个问题呢?请帮忙

鉴于M可以达到200,所以我们可以找到我们可以从特定数字到达的数字。但是我无法解决它因为N可以达到200000.所以如何维护wheather数组是否排序。我认为DP可以提供帮助,但还没有任何线索。

1 个答案:

答案 0 :(得分:0)

这是用于创建算法的解释。

首先将给定X的所有Y组合成一个Map。然后组合给定Y的所有X.这样您最终将数据结构Map<Key,Value>作为Map<Integer,List<Integer>>。例如,如果Q是

1  2
2  3
1  3
4  3

然后,对于X = 1,您将有一个Map条目<1,{2,3}>。所以完整的映射将是

<1,{2,3}>
<2,{1,3}>
<4,{3}>
<3,{1,2,4}>

请注意,由于Q是一个可交换的操作集,我们不需要为2提供两个键,尽管两个键同时显示为X和Y.所以我希望你能够跟进到目前为止,因为整个算法都有在这个映射。然后算法。

package algorithm;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class SortArray {

    /**
     * Given an array and a set of commutative and transitive exchanges, sort the array in
     * increasing order. return the number of operations it took to sort the array.
     * 
     * @param input
     * @param operations
     * @return
     */
    static int sortArray(int[] input, int[][] operations) {
        int ops = 0;
        Map<Integer, Set<Integer>> map = flattenOperations(operations);
        int min = smallestElement(operations);
        for (int i = 0; i < input.length; i++) {
            ops += minimize(i, input, min, map);
        }

        if (isSortedAscending(input)) {
            return ops;
        } else {
            return -1;
        }

    }

    /**
     * the given array is a set of {X,Y} pairs as in {{X0,Y0},{X1,Y1},...}.
     * This method assembles all the Xs for a given Y and then all the Ys for a given X.
     * This is okay because the operations are commutative.
     * 
     * @param operations
     * @return
     */
    static private Map<Integer, Set<Integer>> flattenOperations(int[][] operations) {
        Map<Integer, Set<Integer>> map = new HashMap<Integer, Set<Integer>>();
        // add Ys for X keys
        for (int[] operation : operations) {
            Set<Integer> s = map.get(operation[0]);
            if (s == null) {
                s = new TreeSet<Integer>();
                s.add(operation[1]);
                map.put(operation[0], s);
            } else {
                s.add(operation[1]);
            }
        }

        for (int[] operation : operations) {
            Set<Integer> s = map.get(operation[1]);
            if (s == null) {
                s = new HashSet<Integer>();
                s.add(operation[0]);
                map.put(operation[1], s);
            } else {
                s.add(operation[0]);
            }
        }
        return map;
    }

    static private int smallestElement(int[][] operations) {
        int min = Math.min(operations[0][0], operations[0][1]);
        for (int i = 1; i < operations.length; i++) {
            int tmpMin = Math.min(operations[i][0], operations[i][1]);
            min = Math.min(min, tmpMin);
        }
        return min;
    }

    /**
     * Replace the element at the given index i with the minimum value possible. The minimum value
     * possible is greater than or equal to min but less than the original value at input[i]. If
     * the operation was successful return the number of exchanges, else return 0. This is a
     * subroutine. It makes sense in the greater context. This may not end in a sorted array. But
     * the caller is responsible for checking for sorting at a later point.
     * 
     * 
     * @param i
     * @param input
     * @param min
     * @param map
     * @return
     */
    static private int minimize(int i, int[] input, int min, Map<Integer, Set<Integer>> map) {
        int key = input[i];
        boolean done = false;
        int ops = 0;
        while (!done) {
            int tmp = findSmallestValueLessThanKeyButGreaterThanOrEqualToMin(key, min, map);
            if (-1 != tmp) {
                key = tmp;
                ops++;
            } else {
                done = true;
            }
        }
        input[i] = key;
        return ops;
    }

    /**
     * The lists are assumed to be pre-sorted in ascending order so that the smallest value is at
     * index 0.
     * 
     * @param key
     * @param min
     * @param map
     * @return
     */
    static private int findSmallestValueLessThanKeyButGreaterThanOrEqualToMin(int key, int min, Map<Integer, Set<Integer>> map) {
        Set<Integer> set = map.get(key);
        if (null != set) {
            for (int el : set) {
                if (el < key && el >= min) {
                    return el;
                }
            }
        }
        return -1;
    }

    static private boolean isSortedAscending(int[] input) {
        for (int i = 1; i < input.length; i++) {
            if (input[i - 1] > input[i]) {
                return false;
            }
        }
        return true;
    }

    // ====================================================================================
    public static void main(String[] args) {
        // int[][] Q = { { 1, 2 }, { 2, 3 }, { 1, 3 } };
        // int[] input = { 3, 2, 1 };
        int[][] Q = { { 2, 3 } };
        int[] input = { 2, 1 };

        int ops = sortArray(input, Q);
        System.out.println("Number of operations is " + ops);
        System.out.println("The new array is " + Arrays.toString(input));
    }
}