leetcode hard模式专杀 805. Split Array With Same Average

题干:https://leetcode.com/problems/split-array-with-same-average/

先记输入数组A的平均值为 v

这题别管它题目说得多么花里胡哨,本质上可以转化为,找一个子数组,让这个子数组的平均数等于v即可,可以证明,只要找到了这样一个子数组,那么另外一部分元素组成的子数组平均数也必然等于v。

 

那么可以这样找:先找一个长度为1的子数组,让其平均值等等于v,由于这里只有1个元素,那么直接找存不存在这样一个元素即可。

如果成功,就可以直接返回true了

如果没找成功,继续尝试2,也就是在原始数组中挑选两个元素(各种组合都要试),看看是否存在有一个组合其平均数是v

如果成功,就可以直接放回true了

如果没成功,继续尝试3...

以此类推,尝试到多少为止呢?反正没必要到数组长度,举个例子,因为5选3等价于5选2,所以算到2就够了,所以尝试到(int)Math.floor(A.length/2)即可。

接下来的子问题就转化为: 在一个给定数组中挑k个元素,使其和为kv(因为其平均数为v嘛),这个问题就比较好解了吧。

动态规划即可。状态函数可以抽象表示为dp(k, totalSum, index),定义为从index开始往后的这段内容中,选k个元素,使得总和为totalSum,这样的组合是否存在,存在=true,不存在=false,

那么显然递推公式为: 

dp(k, totalSum, index) = dp(k-1, totalSum-A[index], index+1) || dp(k, totalSum, start+1)

也就是说考虑包含当前index和不包含index两种情况的选择。

 

这个递推公式大家可以纸上展开一下参数,会发现涉及大量重复计算,于是加上一个memtable进行去重。

 

还有几个地方可以做优化,就是题干给出数组元素不会有负数,所以只要是求和,数量是一定只增不减的,那么如果可以判定当前范围元素总和都达不到totalSum,那么就可以直接判false了,不用做更多计算,totalSum如果是负数也可以直接判false

 

递推结束条件为index为数组最后一个index,或者k=1。

 

结合以上方案和优化操作,代码如下:

package com.example.demo.leetcode;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SplitArrayWithSameAverage {
    public boolean splitArraySameAverage(int[] A) {
        double average = calcAverage(A);

        Map> elementIndexMap = new HashMap<>();
        for(int i=0;i memtable = new HashMap<>();

        int maxSelect = (int)Math.floor(A.length/2);
        for(int i=1;i<=maxSelect;i++){
            if(isInteger(average*i)){
                if(existComb(A, i, toNearestInt(average*i), 0, elementIndexMap, memtable)){
                    return true;
                }
            }
        }
        return false;
    }

    private void add2map(Map> mp, Integer key, Integer val){
        if(mp.get(key)!=null){
            mp.get(key).add(val);
        }else{
            List eIndexs = new ArrayList<>();
            eIndexs.add(val);
            mp.put(key, eIndexs);
        }
    }

    private int toNearestInt(double val){
        return (int)Math.floor(val+0.5);
    }

    private boolean isInteger(double val){
        double THRESHOLD = .0001;
        if(Math.ceil(val)-val> elementIndexMap, Map memtable){
        if(memtable.get(k+"_"+totalSum+"_"+start)!=null){
            return memtable.get(k+"_"+totalSum+"_"+start);
        }

        if(totalSum<0 || calcSum(A, start)elementsLeft){
            memtable.put(k+"_"+totalSum+"_"+start,false);
            return false;
        }
        if(elementsLeft==1){
            memtable.put(k+"_"+totalSum+"_"+start,totalSum==A[start]);
            return totalSum==A[start];
        }
        if(k==1){
            List matchedIndexs = elementIndexMap.get(totalSum);
            if(matchedIndexs!=null && matchedIndexs.get(matchedIndexs.size()-1)>=start){
                memtable.put(k+"_"+totalSum+"_"+start,true);
                return true;
            }
            memtable.put(k+"_"+totalSum+"_"+start,false);
            return false;
        }

        //递推公式
        //包含第start个元素的情况和不包含第start个元素的情况
        if(existComb(A, k-1, totalSum-A[start], start+1, elementIndexMap, memtable) || existComb(A, k, totalSum, start+1, elementIndexMap, memtable)){
            memtable.put(k+"_"+totalSum+"_"+start,true);
            return true;
        }

        memtable.put(k+"_"+totalSum+"_"+start,false);
        return false;
    }

    private int calcSum(int[] A, int start){
        int sum = 0;
        for(int i=start;i

 

 

 

你可能感兴趣的:(leetcode,算法,刷题,hard模式)