leetcode 904. Fruit Into Baskets(水果放进篮子)

在这里插入图片描述

水果放进篮子的问题,

fruits数组里面的数字是每个水果的类型,当前位置对应一个水果。

只有2个篮子,每个篮子只能放一种水果,不能几种水果混放,不限制每个篮子放的水果个数。

从左到右遍历数组,可以选择一个开始的index, 从这个index开始,每经历一个数字,都要把该数字类型的水果放进篮子里,那水果不是不能几种类型混着放么,要是和篮子里面的水果类型不匹配怎么办,那就到此为止,结束。

统计两个篮子里最多能放多少个水果。

思路:

方法1: DP

因为只有2个篮子,降低了复杂度,两个篮子就对应前面出现的2种类型(2个数字)。
因为只有2个数字,当无法再继续放的时候(当前数字和前面2个数字不匹配),更新之前放的水果的最大值,然后从头开始(保留前面的一个数字和当前数字,前面第1个数字抛弃)。

这样说可能一头雾水,举个例子吧,
pr1, pr2表示保存的当前两个篮子里面放的类型。
dp表示到当前index时2个篮子里的水果数。
prCnt表示当前index-1处的数字连续出现的个数。这是为了统计当index处的水果和2个篮子里的水果不匹配时,需要从index-1处重新开始放,而index-1处的数字已经连续出现了多少次(前面一种类型的水果已经放了多少个)。

fruits: 3  3  3  1  2  1  1  2  3  3  4
dp:     1  2  3  4  2  3  4  5  2  3  3
pr1:    3  3  3  3  1  2  2  1  2  2  3
pr2:   -1 -1 -1  1  2  1  1  2  3  3  4
prCnt:  1  2  3  1  1  1  2  1  1  2  1
res:    1  2  3  4  4  4  4  5  5  5  5

所以,看明白了吗。刚开始的时候只有pr1=fruits[0], pr2=-1,
dp[0] = 1, 因为这时候只有一个水果,
到第2个的时候,还是3,跟pr1一样,所以pr2还是-1,
prCnt是它前面一个数字3出现的次数,只出现了一次,所以是1,
因为3和pr1相同,所以dp[1] = dp[0] + 1,

第3个3和上面一样,来看看第4个数字1,
它既不等于pr1, 也不等于pr2, 而现在只有pr1有值,pr2还是-1,所以更新pr2=1,
现在它和pr1或pr2相等了,所以dp[3] = dp[2] + 1 = 4,
这是和pr1或pr2相等的情况。

那么再看和pr1和pr2都不相等的情况,第5个数字2,
因为它和前两个篮子的水果都不匹配,所以要放弃类型为3的水果,
取它的前一个类型2,从2开始,
那么2已经连续出现了几次呢,就是prCnt=1
这是dp[4] = prCnt + 1 = 2.

第6个数字1来的时候,它和pr1相等,dp[5] = dp[4] + 1,
但是我们要保证pr1和pr2的顺序(不然可能新出现的数字被放弃了),1现在是最新出现的,应该保存在pr2,
所以把pr1和pr2交换一下顺序。

    public int totalFruit(int[] fruits) {
        int n = fruits.length;
        int res = 1;
        int pr1 = fruits[0], pr2 = -1;
        int[] dp = new int[n];
        int prCnt = 1;

        dp[0] = 1;
        if(n > 1) {
            dp[1] = 2; 
            res = 2; 
            if(fruits[1] != pr1) {
                pr2 = fruits[1];
            } else {
                prCnt = 2;
            }
        }

        for(int i = 2; i < n; i++) {
            if(pr2 == -1 && fruits[i] != pr1) {
                pr2 = fruits[i];
            }
           
            if(fruits[i] == pr1 || fruits[i] == pr2) {
                if(fruits[i] == pr1) {
                    int tmp = pr1;
                    pr1 = pr2;
                    pr2 = tmp;
                }
                if(fruits[i] == fruits[i-1]) prCnt ++;
                else prCnt = 1;
                dp[i] = dp[i-1] + 1;             
            } else {
                dp[i] = prCnt + 1;
                prCnt = 1;
                pr1 = pr2;
                pr2 = fruits[i];
            }

           
            res = Math.max(res, dp[i]);
        }
       
        return res;
    }

方法2: 双指针

上面的DP方法需要统计前一个数字出现的次数prCnt,也可以用双指针记录前一个数字出现的起点和终点,用终点减去起点就是前一个数字出现的次数。
其他的都差不多。

    public int totalFruit(int[] fruits) {
        if (fruits.length == 1) {
            return 1;
        }
        int left = 0;
        int res = 0;
        int[] currFr = new int[]{fruits[0], -1};
        for (int right = 1; right < fruits.length; ++right) {
            int fr = fruits[right];
            if (fr != currFr[0] && fr != currFr[1]) {
                if (currFr[1] == -1) {
                    currFr[1] = fr;
                    continue;
                }
                //统计前一个数字出现的次数
                res = Math.max(res, right - left);
                //更新新的前一个数字起点和终点指针
                int prevFr = fruits[right - 1];
                left = right - 1;
                while (fruits[left - 1] == prevFr) {
                    left--;
                }
                //更新最新的2个数字
                currFr[0] = prevFr;
                currFr[1] = fr;
            }
        }
        return Math.max(fruits.length - left, res);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蓝羽飞鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值