数字n拆分为m个数字之和的组合问题

输入描述:
1、数字n,值为100倍数,范围为100~1000万。

2、数组s,包含20个元素,s=[1000,1500,2000,2500,………,10000,10500](元素为1000~10500内值为500倍数的所有整数)。

3、数组a,包含5个元素,a=[a1,a2,a3,a4,a5],a1~a5均为1~100之内的整数。

问题描述:
1、有一个5列3行的网格grid,初始每格数字均为0,每个格子可重新填入一个数字。现要求从数组s中任意选择8~15个数字(可每次选择相同数字)填入网格(填入位置随意),填完后的每列数字之和记为c1~c5,要求c1xa1+c2xa2+c3xa3+c4xa4+c5xa5=m=n,求满足条件的填充组合。

2、若没有满足条件的组合,则输出最接近n的一种组合,要求该组合计算出的值m不大于n,若数字m有多种组合,随机输出一种。

3、若有多种可能组合,从所有组合中随机输出一种,即保证相同的输入,有概率获得不同输出。

4、若没有满足条件和接近的组合,grid返回全0数组,m返回0。

5、要求时间复杂度不超过O(n^2)(1s至少可以处理1w次输入)。

6、内存空间限制1M。

输出描述:
1、输出填充后的网格数组grid。
2、输出实际的数字m。

这里前提是已知了n,s,a 。 怎么选8-15个数字以什么位置填入网格可以使得计算结果最接近n ?
还是已知n,s,a 和8-15个数字选完后。 怎么放使得计算结果最接近n?

比如:
输入n=78500,
a=[1,3,5,2,2],
s=[1000,1500,2000,...,10500],
可能的返回1:
grid=[
[1000,0,1000],
[2000,1000,3000],
[2500,1500,3500],
[1000,2000,3500],
[3000,0,1000]
],
m=78500;

可能的返回2:
grid = [
[0,0,0],
[2000,1000,3000],
[2500,1500,3500],
[1000,2000,3500],
[3000,1000,1000]
],
m=78500;

很明显,还有很多种满足条件的组合,保证相同输入可以随机返回一种。

(grid每列和与a对应列相乘,然后再累加就是m。)

有概率获得不同输出,这个是古典概型?

n分解成m个正数和所有组合
类似的,可以看看

std::vector<std::vector<int> > Permute::sumNToDst(int dst, int n)
{
    std::vector<std::vector<int> > r;
    findUpSeq(1,n,dst,r);
    std::cout<<r.size()<<" solutions "<<std::endl;
    for(int i=0;i<r.size();i++){
        for(int j=0;j<r.at(i).size();j++){
            std::cout<<r.at(i).at(j)<<" ";
        }
        std::cout<<std::endl;
    }
}


/**
 * @brief Permute::findUpSeq
 * @param low       盒子中最少放置的球数
 * @param num       盒子数
 * @param sum       球总数
 * @param result    返回二维数组分配结果
 * @return          是否分配成功
 */
int Permute::findUpSeq(int low, int num,int sum, vector<vector<int> > &result)
{
    result.clear();
    if(num<1||sum<0)
        return 0;
    //只有一个盒子
    if(num==1){
        if(sum>=low){
            std::vector<int> v;
            v.push_back(sum);
            result.push_back(v);
            return 1;
        }
        return 0;
    }else if(num<1){
        return 0;
    }
    //球数少于每个盒子应放总数之和
    if(sum<num*low)
        return 0;
    else if(sum == num*low){
        std::vector<int> t;
        t.assign(num,low);
        result.push_back(t);
        return low;
    }

    std::vector<std::vector<int> > s;
    //依次给1号盒子放1-n/m个球,将剩下的球放到剩下的盒子中且保证后续盒子球数不低于前一盒子
    for(int i=low;((i*num)<sum+1);i++){
        s.clear();
        //如果后续盒子放置成功,将其放置方案中加入1号盒子中所放球数做为一个方案
        if(findUpSeq(i,num-1,sum-i,s)){
            for(int j=0;j<s.size();j++){
                s.at(j).push_back(i);
                result.push_back(s.at(j));
                for(int k=0;k<s.at(j).size();k++){
                    std::cout<<" "<<s.at(j).at(k);
                }
            }
            std::cout<<std::endl;

        }
    }
    return 1;
}

第一个:想到了一个大大降低循环次数的方法,但是如果要得出精确值还是得很大循环。
第二个:还有一个降维的方法,求估值
算精确值的思路大概这样,看能不能结合题主的查表:
第1步. 数组s,包含20个元素,除以500降数值到s1

s= [1000,15002000,2500,………,10000,10500]
s1 = [2,3,4,5,6,.....21]

第2步. 把3行5列的格子整合起来看,3个格子单独组合虽然有8000种,但是3个格子加起来最多只有2-63种,大概60种组合,有可能更小可以预先算好,
第3步. 把5个列p5的大概60种组合和c1 c2 ... c5循环算出精确值d5 刚好为n,或者差不多的
第4步. 通过d5来确认每一列的3个数


降维估计的方法如下:
第1步:把c1,c2,c3,c4,c5按照从小到大的顺序进行排序
第2步:把a1-a2-a3-a4-a5,可以分层2组或者3组或者4组,比如如果2组 第一组为a1=a2=a3,第二组为a4=a5
第3步:那么如果是2组就降维成了(c1+c2+c3)*a1和(c4+c5)*a4, 这样就是60 * 60的复杂度
第4步:上面算出来有误差,需要精度补齐,因为c1到c5是从小到大排列了,所有一个步长c1最小,c5最大,
所以先从c5开始补齐,直到补齐到c1进行补数,找到最接近的值
第5步:把上面算出来的a1-a5分解到每一个格子
还可以降维到3维,4维等,然后精度降为 |n-m| < delta
降维的可以多想想

package com.yctx;

import java.util.*;

public class Test {
    public static Map<Integer, String> numMap = new HashMap<>();
    public static List<Integer> alist = new ArrayList<>();
    public static List<List<Integer>> agreplist = new ArrayList<>();
    public static Boolean end = false;

    public static void main(String[] args) {
        List<Integer> s = new ArrayList<>();
        s.add(0);
        //统一÷500
        for (int i = 2; i <= 21; i++) {
            s.add(i);
        }
        System.out.println(s);
//        获取所有c1 可能的值与组合
        for (Integer num1 : s) {
            for (Integer num2 : s) {
                for (Integer num3 : s) {
                    numMap.put(num1 + num2 + num3, (num1 * 500) + "_" + (num2 * 500) + "_" + (num3 * 500));
                }
            }
        }
        System.out.println(numMap.size());
        System.out.println(numMap);
        List<Integer> a = new ArrayList<>();
        a.add(1);
        a.add(3);
        a.add(5);
        a.add(2);
        a.add(2);
        alist.addAll(a);
        int p[] = {1, 3, 5, 2, 2};
        perm(p, new Stack<>());
        Integer n = 78400;
        Integer m = n / 500;
        end = false;
        for (int j = m; j >= 0; j--) {
            if (!end) {
                getnum(j);
            }
        }

    }

    public static void getnum(int m) {
        //将c1xa1+c2xa2+c3xa3+c4xa4+c5xa5=m
        /**
         * 将c1xa1+c2xa2+c3xa3+c4xa4+c5xa5=m 简化
         * a+b+c+d+e=m 由于 从数组s中任意选择8~15个  设a_e 降序排列知
         * 极限状态为 m-2,1,1,0,0
         */


        System.out.println("m:" + (m*500));
        List<Integer> numList = new ArrayList<>();
        int x = 0;
        for (int a = m - 2; a > 1; a--) {

            if (!isNum(a)) {
                continue;
            }
            for (int b = m - a - 1; b > 0; b--) {
                if (!isNum(b) || a < b) {
                    continue;
                }
                for (int c = m - a - b; c > 0; c--) {
                    if (!isNum(c) || b < c) {
                        continue;
                    }
                    for (int d = m - a - b - c; d >= 0; d--) {
                        if (!isNum(d) || c < d) {
                            continue;
                        }
                        int e = m - a - b - c - d;
                        if (d < e) {
                            continue;
                        }
                        numList.clear();
//                        System.out.println("a:" + a + " b:" + b + " c:" + c + " d:" + d + " e:" + e + " sum:" + (a + b + c + d + e));
                        numList.add(a);
                        numList.add(b);
                        numList.add(c);
                        numList.add(d);
                        numList.add(e);
                        List<Integer> alist = getgroup(numList);
                        if (alist != null) {
                            System.out.println("a:" + a + " b:" + b + " c:" + c + " d:" + d + " e:" + e + " sum:" + (a + b + c + d + e));
                            System.out.println("alist " + alist);
                            System.out.println(a / alist.get(0) + "*" + alist.get(0) + "+" + b / alist.get(1) + "*" + alist.get(1) + "+" + c / alist.get(2) + "*" + alist.get(2) + "+" + d / alist.get(3) + "*" + alist.get(3) + "+" + e / alist.get(4) + "*" + alist.get(4));
                            System.out.println(numMap.get(a / alist.get(0)));
                            System.out.println(numMap.get(b / alist.get(1)));
                            System.out.println(numMap.get(c / alist.get(2)));
                            System.out.println(numMap.get(d / alist.get(3)));
                            System.out.println(numMap.get(e / alist.get(4)));
                            end = true;
                            return;
                        }
                    }
                }
            }
        }
    }

    /**
     * 如果 当前值等于 a*c 返回true
     *
     * @param num
     * @return
     */
    public static boolean isNum(int num) {
//        System.out.println("-----"+num);
        for (Integer num1 : alist) {
            if (num % num1 == 0 && numMap.containsKey(num / num1)) {
                return true;
            }
        }
        return false;
    }

    /**
     * a:125 b:16 c:10 d:3 e:3 sum:157
     *
     * @param numList
     * @return
     */
    public static List<Integer> getgroup(List<Integer> numList) {
        for (List<Integer> alist : agreplist) {
            boolean t = false;
            for (int i = 0; i < 5; i++) {
                if (numList.get(i) % alist.get(i) == 0 && numMap.containsKey(numList.get(i) / alist.get(i))) {
                } else {
                    t = true;
                }
            }
            if (!t) {
                System.out.println(alist);
                return alist;
            }
        }

        return null;
    }

    /**
     * 获取 a全排列
     * @param array
     * @param stack
     */
    public static void perm(int[] array, Stack<Integer> stack) {//利用循环递归,将数据存入栈中
        if (array.length <= 0) {    //创造递归结束条件
            //进入了叶子节点,输出栈中内容
            Stack<Integer> stackcopy = (Stack<Integer>) stack.clone();  //克隆一个栈stackcopy,用于对栈进行相关操作,而不改变原栈内容
            int length = stack.size();
            Integer a[] = new Integer[length];      //定义一个长度为栈的长度的数组,用于存储栈的内容
            for (int j = 0; j < length; j++) {
                a[j] = stackcopy.peek();    //先取出栈顶元素到数组
                stackcopy.pop();            //然后将栈顶元素出栈
            }
            //判断数组是否满足条件
//            System.out.println(Arrays.toString(a));//该数组满足条件,打印输出满足条件的数组
            List<Integer> list = Arrays.asList(a);
            agreplist.add(list);
//            list.addAll(a);
        } else {
            for (int i = 0; i < array.length; i++) {    //后续就是tempArray.length
                //tmepArray是一个临时数组
                //例如:123的全排列,先取出1,那么这时tempArray中就是23
                int[] tempArray = new int[array.length - 1];//以a[i]为界线,tempArray存储a[i+1]~a[array.length-1]的数据
                System.arraycopy(array, 0, tempArray, 0, i);
                System.arraycopy(array, i + 1, tempArray, i, array.length - i - 1);
                stack.push(array[i]);//1;array[i]依次入栈
                perm(tempArray, stack);  //递归,tempArray相对于array
                stack.pop();        //栈顶元素出栈,还原array数组,以便进行下一次循环的操作


            }
        }
    }
}


m=n看标题就知道都是1😂