【java/算法/组合】从1,2,3......20这20个数中任取2数,使其和能被4整除的取法有几种?

【题干】从1,2,3......20这20个数中任取2数,使其和能被4整除的取法有几种?

【数学解法】

20个数可以分为4类:

4n+1类:1,5,9,13,17

4n+2类:2,6,10,14,18

4n+3类:3,7,11,15,19

4n+4类:4,8,12,16,20

若两数全在4n+4或是4n+2中,两数和无疑是可以被4整除的,取法有2*C52=2*10=20种;

若一数在4n+1中,一数在4n+3中,两数和也是可以被4整除的,取法有5*5=25种;

Sum=20+25=45种

【代码解法】

思路,使用选择器从20数种选2,以和整除4为0为过滤器,可得到所有的取法。

DivideFour类代码:

package test230422;

import java.util.List;

public class DivideFour {
    public static void main(String[] args) {
        final int LEN=20;
        Integer[] arr=new Integer[LEN];
        for(int i=1;i<=LEN;i++) {
            arr[i-1]=i;
        }
        
        Combiner2<Integer> c=new Combiner2<Integer>(arr,2);
        List<List<Integer>> results=c.getResults();
        
        int idx=0;
        for(List<Integer> res:results) {
            int first=res.get(0);
            int second=res.get(1);
            int sum=first+second;
            
            if(sum%4==0) {
                System.out.println(String.format("%02d", ++idx) +".["+first+","+second+"]");
            }
        }
    }
}

Combiner2类:

package test230422;

import java.util.ArrayList;
import java.util.List;

/**
 * 从数组中选择有限个数的组合器,支持泛型
 * 比如从abc中选2个,会产生ab,ac,bc的结果
 */
public class Combiner2<T> {
    /**
     * 选择的结果
     */
    private List<List<T>> results;
    
    /**
     * 构造函数
     * @param candidates 候选者名单
     * @param selectCnt 选择的数量
     */
    public Combiner2(T[] candidates,int selectCnt) {
        int nCnt = candidates.length;
        
        if(selectCnt>nCnt) {
            throw new ArrayIndexOutOfBoundsException(selectCnt+">"+nCnt);
        }
        
        results=new ArrayList<>();

        int nBit = (0xFFFFFFFF >>> (32 - nCnt));

        for (int i = 1; i <= nBit; i++) {
            List<T> ls=new ArrayList<>();
            
            for (int j = 0; j < nCnt; j++) {
                if ((i << (31 - j)) >> 31 == -1) {
                    ls.add(candidates[j]);
                }
            }

            if(ls.size()==selectCnt) {
                results.add(ls);
            }
        }
    }
    
    /**
     * 取得选择的结果
     * @return
     */
    public List<List<T>> getResults(){
        return results;
    }
    
    /**
     * 使用示例
     * @param args
     */
    public static void main(String[] args) {
        // 1,2,3中选2
        Integer[] arr= {1,2,3};
        Combiner2<Integer> c1=new Combiner2<Integer>(arr,2);
        
        List<List<Integer>> results=c1.getResults();
        for(List<Integer> ls:results) {
            System.out.println(ls);
        }
        
        // "甲","乙","丙","丁"中选3
        String[] arr2= {"甲","乙","丙","丁"};
        Combiner2<String> c2=new Combiner2<String>(arr2,3);
        
        List<List<String>> results2=c2.getResults();
        for(List<String> ls:results2) {
            System.out.println(ls);
        }
    }
}

【输出】

01.[1,3]
02.[3,5]
03.[2,6]
04.[1,7]
05.[5,7]
06.[4,8]
07.[3,9]
08.[7,9]
09.[2,10]
10.[6,10]
11.[1,11]
12.[5,11]
13.[9,11]
14.[4,12]
15.[8,12]
16.[3,13]
17.[7,13]
18.[11,13]
19.[2,14]
20.[6,14]
21.[10,14]
22.[1,15]
23.[5,15]
24.[9,15]
25.[13,15]
26.[4,16]
27.[8,16]
28.[12,16]
29.[3,17]
30.[7,17]
31.[11,17]
32.[15,17]
33.[2,18]
34.[6,18]
35.[10,18]
36.[14,18]
37.[1,19]
38.[5,19]
39.[9,19]
40.[13,19]
41.[17,19]
42.[4,20]
43.[8,20]
44.[12,20]
45.[16,20]

数学解法和程序解法可以相互印证。

END

posted @ 2023-04-22 15:04  逆火狂飙  阅读(153)  评论(0编辑  收藏  举报
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东