646. Maximum Length of Pair Chain

646. Maximum Length of Pair Chain
找到符合规则的最长chain。
weekly contest 42的第二题。
我一看感觉很简单,就是dfs,但是总也写不出,各种Wrong Answer
最后想了一下,这个就是类似Permutation的「排列」问题。晚上整理了一下,写了下面的代码,总算不Wrong Answer了,但是TLE了,不过思路是比较清晰的:

我的DFS解法(TLE):

//注意,以下代码会TLE:
    int max = 1;

    public int findLongestChain(int[][] pairs) {
        if (pairs == null || pairs.length == 0) return 0;
        chainDfs(pairs, new boolean[pairs.length], 0, Integer.MIN_VALUE);
        return max;
    }

    private void chainDfs(int[][] pairs, boolean[] used, int count, int b) {
//不需要terminator
        for (int i = 0; i < pairs.length; i++) {
            if (!used[i] && pairs[i][0] > b) {
                used[i] = true;
                max = Math.max(count + 1, max);
                chainDfs(pairs, used, count + 1, pairs[i][1]);
                used[i] = false;
            }
        }
    }

既然TLE了,就说明有可以优化的地方。
这题有个条件是In every pair, the first number is always smaller than the second number. 这说明,如果我们以第一个数字排序,那后一个数组[a,b]的第二个数字(b)是一定大于前一个数组[c,d]的的第一个数字(c)的。这样可以减少很多次递归。

优化了一下:


    int max = 1;

    public int findLongestChain(int[][] pairs) {
        if (pairs == null || pairs.length == 0) return 0;
//      Arrays.sort(pairs, (a, b) -> a[1] - b[1]);
        Arrays.sort(pairs, new Comparator() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });
        chainDfs(0, pairs, new boolean[pairs.length], 0, Integer.MIN_VALUE);
        return max;
    }

    private void chainDfs(int index, int[][] pairs, boolean[] used, int count, int b) {
        if (index == pairs.length)
            return;
        for (int i = index; i < pairs.length; i++) {
            if (!used[i] && pairs[i][0] > b) {
                used[i] = true;
                max = Math.max(count + 1, max);
                chainDfs(i + 1, pairs, used, count + 1, pairs[i][1]);
                used[i] = false;
            }
        }
    }

可是万万没想到,仍然TLE...test case确实非常大,用DFS的话栈深度不可想象。

看了下大家的discussion,有两种算法,一种是Greedy,说是跟CLRS上的activity selection几乎一模一样(还有interval 问题)。。顿时觉得自己基础差。

另一种是DP。今天先到这儿了。

--

26 July Update

GREEDY(ITERATION)

复习了一下activity selection问题,发现这题跟那个几乎是一致的。写了一下代码,但是我其实并不能说服自己greedy得到的答案就是正确的,这一点跟dp不太一样,dp是有理有据的,而greedy仿佛是需要证明的,据说greedy经常用剪枝来证明,fine,又一个我认识他他不认识我的名词。。今天先到这,这题还没完。

    public int findLongestChain(int[][] pairs) {
        if (pairs == null || pairs.length == 0) return 0;
        int count = 1;
        Arrays.sort(pairs, new Comparator() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        });

        int index = 0;
        for (int i = 1; i < pairs.length; i++) {
            if (pairs[i][0] > pairs[index][1]) {
                count++;
                index = i;
            }
        }
        return count;
    }

27 July Update

GREEDY(RECURSION)

看了算法导论上的活动选择问题,这题还可以用递归来做,或者说,所有的for循环都能改成递归?
这题的递归实现:

    public int findLongestChain(int[][] pairs) {
        if (pairs == null || pairs.length == 0) return 0;
        Arrays.sort(pairs, new Comparator() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        });
        recursion(pairs, 0);
        return cnt;
    }

    int cnt = 1;

    private void recursion(int[][] pairs, int index) {
        int m = index + 1;
        while (m < pairs.length && pairs[index][1] >= pairs[m][0]) {
            m++;
        }
        if (m < pairs.length) {
            cnt++;
            recursion(pairs, m);
        }
    }

DP

看了另外有人用DP做的,但是这题用DP有点浪费了,它这个方法跟Weighted Job Scheduling Dynamic Programming的思路是一致的,工作安排比这个多一个要考虑的地方,就是不同的工作既要不冲突,而且因为每份工作的报酬不一样,不能简单地只考虑完成时间最短,所以只能用DP。那这题跟Job Schedule唯一的不同就是在if判断的地方,这题是dp[i] < dp[j] + 1,如果是Job Schedule就要是dp[i] < dp[j] + profit[i]。
两重循环的解法让人想起LIS PROBLEM(JOB SCHEDULE跟LIS的判断方法一模一样)。

public class Solution {
    public int findLongestChain(int[][] pairs) {
        Arrays.sort(pairs, (a, b) -> (a[0] - b[0]));
        
        int i, j, max = 0, n = pairs.length;
        int dp[] = new int[n];
      
        for (i = 0; i < n; i++) dp[i] = 1;
        
        for (i = 1; i < n; i++)
            for (j = 0; j < i; j++)
                if (pairs[i][0] > pairs[j][1] && dp[i] < dp[j] + 1)
                    dp[i] = dp[j] + 1;

        for (i = 0; i < n; i++) if (max < dp[i]) max = dp[i];
        
        return max;
    }
}

疑问:用DP为什么要sort,为什么按照开始时间和结束时间sort都能AC,不sort就无法AC?

另外,这题的GREEDY,我还是不太懂得如何证明,算法导论上有证明,我没细看。据说通常用剪枝证明GREEDY。

ref:
http://www.cnblogs.com/hapjin/p/5573419.html
http://www.jianshu.com/p/293a4056a50a

你可能感兴趣的:(646. Maximum Length of Pair Chain)