LintCode867、四键键盘问题(dp数组的定义)

题目描述

这道题在leetcode要会员,所以我去了lintcode做了。
https://www.lintcode.com/problem/867/

在这里插入图片描述

解法

第一次解法
第一次觉得挺好做的,于是写出来如下代码:

public class Solution {
    /**
     * @param N: an integer
     * @return: return an integer
     */
    public int maxA(int N) {
        // write your code here
        if(N<0) return -1;
        if(N<=3) return N;
        int []dp = new int[N+1];
        dp[0]=0;
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        int cache=0;//记录缓冲区的数据量大小
        for(int i=4;i<=N;i++){
            //要保证最大,
            if(dp[i-3]>=3 && i-3>=3 && dp[i-3]>3*cache){//是否能按下c-A\c-c\c-v,同时判断按下3次c-V的收益大还是按下3次c-A\c-c\c-v的收益大
                dp[i] = 2*dp[i-3];//全选、复制、黏贴
                cache = dp[i-3];
              //  System.out.println(cache+" --");
            }else if(cache!=0){
                dp[i] = dp[i-1]+cache;//只剩下一次机会了

            }else{
                //只能一个一个按
                dp[i] = dp[i-1]+1;
            }
        }
        return dp[N];
	}
}

实际上,这样是不对的,因为前一个状态的选择,与后面状态的选择不是依赖关系,而是一种最大选择关系。比如6次,则A,A,A,C-A,C-C,C-V,得到6个A,但是我们按7次的话,则第7次直接按下C-V就得到了9个A。

但是如果我们现在可以按100次,那可能按第10次的结果和我们只能按10次的结果不一样。

第二次,暴力破解——递归

既然直接思考不行,我们就需要先以递归的方法来思考,暴力破解法:

public class Solution {
    /**
     * @param N: an integer
     * @return: return an integer
     */
    public int maxA(int N) {
        return findMaxA(N,0,0);
    }
    public int findMaxA(int times,int A_nums,int cacheNums){
        if(times<=0) return A_nums;
        return Math.max(
            //按下A
            findMaxA(times-1,A_nums+1,cacheNums),
            Math.max(
                    //按下C-V
                    findMaxA(times-1,A_nums+cacheNums,cacheNums),
                    //按下C-A,C-C
                    findMaxA(times-2,A_nums,A_nums)//缓冲区数目直接变为A_nums
                    )

        );

    }
}

在这里插入图片描述
结果超时,因为子问题重叠的可能太多了。因为这个问题存在:

如果我们现在可以按100次,那可能按第10次的结果和我们只能按10次的结果不一样。

所以使用备忘录的话,要使用三元组避免复用。所以这里就不写了。

第三次做法——重新定义dp数组
我们可以知道:
要么一直按A:A,A,…A(当 N 比较小时)。

要么是这么一个形式:A,A,…C-A,C-C,C-V,C-V,…C-V(当 N 比较大时)。

那么我们思考动态递归的过程:

  • 变量是什么?按键的次数、A的个数
  • dp【i】表示按键i次的显示A的最大结果

思考选择:

  • 我们最后一次的按键要么是按下A,要么是按下C-V进行直接拷贝或者C-A,C-C,C-V。
    对于按下A的情况,我们知道就是上一次的状态上多加了一个A,即dp[i]=dp[i-1]+1;

  • 对于按下C-V的情况呢?我们知道它前面必然出现了C-A,C-C,才能将数据拷贝到缓冲区,我才能用C-V。但是它的位置不确定。那么我们就必须记录C-V的最早的起点,那么此时该起点前面的两次操作必然就是C-A,C-C。这个起点的情况只能在i之前,所以我们直接暴力!

然后我们两种选择里面,取最大的那个结果即得到了当前的dp【i】。

int[]dp = new int[N+1];
dp[0] = 0;
for(int i=1;i<=N;i++){
	//选择按下A,
	dp[i] = dp[i-1]+1;
	//选择最后一次操作为按下C-V
	for(int j=2;j<i;j++){//j必然大于2才可能按下c-v,记录C-V的最早的起点
		//这里全选+复制,那么我们得到了 dp(j-2)缓冲区数目,然后连续按下复制键j-i+1次
		dp[i] = Math.max(dp[i],dp[j-2]*(i-j+1));
		//这里就是暴力遍历,假设每个j都是C-V的最早的起点,找到最大值
	}
}

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨夜※繁华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值