LeetCode——四键键盘

Q:假设你有一个特殊的键盘包含下面的按键:
Key 1: (A):在屏幕上打印一个 'A'。
Key 2: (Ctrl-A):选中整个屏幕。
Key 3: (Ctrl-C):复制选中区域到缓冲区。
Key 4: (Ctrl-V):将缓冲区内容输出到上次输入的结束位置,并显示在屏幕上。
现在,你只可以按键 N 次(使用上述四种按键),请问屏幕上最多可以显示几个 'A'呢?

样例 1:
输入: N = 3
输出: 3
解释:
我们最多可以在屏幕上显示三个'A'通过如下顺序按键:
A, A, A

样例 2:
输入: N = 7
输出: 9
解释:
我们最多可以在屏幕上显示九个'A'通过如下顺序按键:
A, A, A, Ctrl A, Ctrl C, Ctrl V, Ctrl V

注释:
1 <= N <= 50
结果不会超过 32 位有符号整数范围。

A:
状态转移:

写出代码:

    public int maxA(int N) {
        if (N <=1)
            return N;
        int a_num = 0;
        int copy = 0;
        return dp(N, a_num, copy);
    }

    private int dp(int n, int a_num, int copy) {
        if (n <= 0)//注意这里一定是<=0
            return a_num;
        return max(dp(n - 1, a_num + 1, copy), dp(n - 1, a_num + copy, copy), dp(n - 2, a_num, a_num));
    }

    private int max(int a, int b, int c) {
        return Math.max(Math.max(a, b), c);
    }

但这样会超时。我们用memo记录一下:

    Map<int[], Integer> memo = new HashMap<>();

    public int maxA(int N) {
        if (N <= 1)
            return N;
        int a_num = 0;
        int copy = 0;
        return dp(N, a_num, copy);
    }

    private int dp(int n, int a_num, int copy) {
        if (n <= 0)
            return a_num;
        int[] array = new int[]{n, a_num, copy};
        if (memo.containsKey(array))
            return map.get(array);
        int max = max(dp(n - 1, a_num + 1, copy), dp(n - 1, a_num + copy, copy), dp(n - 2, a_num, a_num));
        memo.put(array, max);
        return max;
    }

    private int max(int a, int b, int c) {
        return Math.max(Math.max(a, b), c);
    }

还是超时了。

考虑第二种写法:
这个算法基于这样⼀个事实,最优按键序列⼀定只有两种情况:要么⼀直按 A :A,A,...A(当 N ⽐较⼩时)。要么是这么⼀个形式:A,A,...C-A,C-C,C-V,C-V,...C-V(当 N ⽐较⼤时)。因为字符数量少(N ⽐较⼩)时, C-A C-C C-V 这⼀套操作的代价相对⽐较⾼,可能不如⼀个个按 A ;⽽当 N ⽐较⼤时,后期 C-V 的收获肯定很⼤。这种情况下整个操作序列⼤致是:开头连按⼏个 A ,然后 C-A C-C组合再接若⼲ C-V ,然后再 C-A C-C 接着若⼲ C-V ,循环下去。换句话说,最后⼀次按键要么是 A 要么是 C-V 。
由于最优的操作序列⼀定是 C-A C-C 接着若⼲ C-V ,所以我们⽤⼀个变量 j 作为若⼲ C-V 的起点。那么 j 之前的 2 个操作就应该是 C-A C-C 了:

    public int maxA(int N) {
        if (N <= 1)
            return N;
        int[] dp = new int[N + 1];
        dp[0] = 0;
        for (int i = 1; i <= N; i++) {
            dp[i] = dp[i - 1] + 1;//按 A 键
            //全选 & 复制 dp[j-2],连续粘贴 i - j 次
            for (int j = 2; j < i; j++) {
                //屏幕上共 dp[j - 2] * (i - j + 1) 个 A
                dp[i] = Math.max(dp[i], dp[j - 2] * (i - j + 1));
            }
        }
        return dp[N];
    }

猜你喜欢

转载自www.cnblogs.com/xym4869/p/12591717.html