【数学解法】
先把7天分4段,每段1到2天。
可以试着分一下,发现只允许一段分配一天,另外三段都是两天。
如果有两段一天,那么剩下五天分两段,怎么分都超过限制。
三段都一天更不可能。
故7天必备分成2,2,2,1这四段。
1这段从7选1过来,C_7_1;
剩下三段是C_6_2*C_4_2*C_2_2;
四段分四人,是C_4_1*C_3_1*C_2_1*C_1_1=A_4_4,这步实际上就是四人的全排列;
综上,分配方案分两步:一步七天分四段,二步四段分四人。
Sum=(C_7_1*C_6_2*C_4_2*C_2_2)*(A_4_4)=630*24=15120
故总方案有15120种。
网上此题还有一种解法是2520种方案,但经过程序验证可以发现15120种是正确答案。
【代码解法】
辅助类Arranger,此类用来作排列:
package test230916; import java.util.ArrayList; import java.util.List; /** * 用于产生排列结果的工具类 * 从n个元素中取出m个元素,按照一定的顺序排成一列。得到所有排列的方案 */ class Arranger { // 保存在内部的对原始元素数组的引用 private int[] arr; // 总计多少元素,此即数组长度 private final int n; // 选多少个 private final int m; // 返回结果 private List<List<Integer>> results; /** * 构造函数一 * 这个构造函数是用于全排列的(n=m=数组长度) * * @arr 原始元素数组 */ public Arranger(int[] arr) { this.arr = arr; this.n = arr.length; this.m = arr.length; this.results = new ArrayList<>(); doArrange(new ArrayList<>()); } /** * 构造函数二 * 这个构造函数是用于部分排列的(m<n=数组长度) * * @param arr 原始元素数组 * @param selCnt 选多少个 */ public Arranger(int[] arr, int selCnt) { this.arr = arr; this.n = arr.length; this.m = selCnt; if (m > n) { throw new ArrayIndexOutOfBoundsException("m:" + m + " >n:" + n); } this.results = new ArrayList<>(); doArrange(new ArrayList<>()); } /** * 使用递归进行全排列,结果放在results中 * * @param initialList 初始链表 */ private void doArrange(List<Integer> initialList) { List<Integer> innerList = new ArrayList<>(initialList); if (m == initialList.size()) { results.add(innerList); } for (int i = 0; i < arr.length; i++) { if (innerList.contains(arr[i])) { continue; } innerList.add(arr[i]); doArrange(innerList); innerList.remove(innerList.size() - 1); } } /** * 获得结果链表的引用 * * @return */ public List<List<Integer>> getResults() { return results; } // 测试 public static void main(String[] args) { int[] numbers = {1, 2, 3, 4}; Arranger arranger = new Arranger(numbers); System.out.println("四元素全排列示例:"); int idx = 0; for (List<Integer> re : arranger.getResults()) { System.out.println(String.format("%02d", ++idx) + "." + re); } /*Arranger arranger2 = new Arranger(numbers, 2); System.out.println("\n四选二排列示例:"); idx = 0; for (List<Integer> re : arranger2.getResults()) { System.out.println(String.format("%02d", ++idx) + "." + re); }*/ } }
辅助类Combination,此类用于作组合:
package test230916; import java.util.ArrayList; import java.util.List; import java.util.Stack; /** * 数学中排列组合中的组合器实现 * 传入一个数组及选择的个数,传出所有选择方案 */ class Combination { /** * 用于存放中间结果 */ private Stack<Integer> stack; /** * 用于存放结果 */ private List<List<Integer>> results; /** * 构造函数 * @param arr 进行组合的元素 * @param count 选多少个 */ public Combination(int[] arr,int count) { if(count>arr.length) { throw new ArrayIndexOutOfBoundsException(count+">"+arr.length); } stack = new Stack<>(); results=new ArrayList<>(); doSelect(arr,count,0,0); } /** * 进行选择 * @param arr 目标数组 * @param expect 期望选择数量 * @param actual 实际选择数量 * @param current 当前下标 */ private void doSelect(int[] arr, int expect, int actual, int current) { if(actual == expect) { List<Integer> list=new ArrayList<>(); for(int i:stack) { list.add(i); } results.add(list); return; } for(int i=current;i<arr.length;i++) { if(!stack.contains(arr[i])) { stack.add(arr[i]); doSelect(arr, expect, actual+1, i); stack.pop(); } } } /** * 取得组合结果 * @return */ public List<List<Integer>> getResults(){ return results; } /** * 测试 * @param args */ public static void main(String[] args) { final int[] arr= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30}; final int count=2; Combination c=new Combination(arr,count); List<List<Integer>> results=c.getResults(); int idx=0; for(List<Integer> res:results) { System.out.println(String.format("%02d", ++idx) +"."+res); } } }
主类DutyPlan:
package test230916; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.List; /** * 将国庆/春节七天假期分四段,每段一到两天,分给四人值班,看具体有几种方案 * @author 逆火 * @date 230916 */ public class DutyPlan { public static void main(String[] args) { int idx = 0; List<String> sentences=new ArrayList<>(); final String[] names={"甲","乙","丙","丁"}; int[] idxes = {0,1, 2, 3,}; Arranger arranger = new Arranger(idxes); // 代表假期第几天 final int[] arr1= {1,2,3,4,5,6,7}; Combination c1=new Combination(arr1,1); List<List<Integer>> results1=c1.getResults(); for(List<Integer> result1:results1) { // result1=C_7_1 int[] arr2=minus(arr1,result1); //System.out.println(getArrDesc(arr2)); Combination c2=new Combination(arr2,2); List<List<Integer>> results2=c2.getResults(); for(List<Integer> result2:results2) { // result1=C_6_2 int[] arr3=minus(arr2,result2); //System.out.println(getArrDesc(arr3)); Combination c3=new Combination(arr3,2); List<List<Integer>> results3=c3.getResults(); for(List<Integer> result3:results3) { // result3=C_4_2 int[] arr4=minus(arr3,result3); // arr4=C_2_2 for (List<Integer> re : arranger.getResults()) { //System.out.print(String.format("%02d.", ++idx)); //System.out.print(names[re.get(0)]+"值第"+result1+"天,"); //System.out.print(names[re.get(1)]+"值第"+result2+"天,"); //System.out.print(names[re.get(2)]+"值第"+result3+"天,"); //System.out.println(names[re.get(3)]+"值第"+getArrDesc(arr4)+"天."); String sentence=String.format("%02d.", ++idx); sentence+=names[re.get(0)]+"值第"+result1+"天,"; sentence+=names[re.get(1)]+"值第"+result2+"天,"; sentence+=names[re.get(2)]+"值第"+result3+"天,"; sentence+=names[re.get(3)]+"值第"+getArrDesc(arr4)+"天."; sentences.add(sentence); } } } } try { FileOutputStream writerStream = new FileOutputStream("c:\\hy\\DutyPlan230916.txt"); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(writerStream, "UTF-8")); for(String s:sentences) { writer.write(s+"\n"); } writer.close(); }catch(Exception ex) { ex.printStackTrace(); } System.out.println("End"); } /** * 返回整形数组的描述,打印数组时用的 * @param arr * @return */ private static String getArrDesc(int[] arr) { String[] strs=new String[arr.length]; int idx=0; for(int i:arr) { strs[idx++]=""+i; } return "["+String.join(",", strs)+"]"; } /** * 返回存在于arr中,不存在于result的元素组成的数组 * @param arr * @param result * @return */ private static int[] minus(int[] arr,List<Integer> result) { int[] retval=new int[arr.length-result.size()]; int idx=0; for(int i:arr) { if(result.contains(i)==false) { retval[idx++]=i; } } return retval; } }
输出总15120种方案(下载地址:https://files.cnblogs.com/files/heyang78/DutyPlan230916.rar?t=1694855168&download=true),以下是截取的部分:
15091.丁值第[7]天,甲值第[5, 6]天,乙值第[2, 4]天,丙值第[1,3]天. 15092.丁值第[7]天,甲值第[5, 6]天,丙值第[2, 4]天,乙值第[1,3]天. 15093.丁值第[7]天,乙值第[5, 6]天,甲值第[2, 4]天,丙值第[1,3]天. 15094.丁值第[7]天,乙值第[5, 6]天,丙值第[2, 4]天,甲值第[1,3]天. 15095.丁值第[7]天,丙值第[5, 6]天,甲值第[2, 4]天,乙值第[1,3]天. 15096.丁值第[7]天,丙值第[5, 6]天,乙值第[2, 4]天,甲值第[1,3]天. 15097.甲值第[7]天,乙值第[5, 6]天,丙值第[3, 4]天,丁值第[1,2]天. 15098.甲值第[7]天,乙值第[5, 6]天,丁值第[3, 4]天,丙值第[1,2]天. 15099.甲值第[7]天,丙值第[5, 6]天,乙值第[3, 4]天,丁值第[1,2]天. 15100.甲值第[7]天,丙值第[5, 6]天,丁值第[3, 4]天,乙值第[1,2]天. 15101.甲值第[7]天,丁值第[5, 6]天,乙值第[3, 4]天,丙值第[1,2]天. 15102.甲值第[7]天,丁值第[5, 6]天,丙值第[3, 4]天,乙值第[1,2]天. 15103.乙值第[7]天,甲值第[5, 6]天,丙值第[3, 4]天,丁值第[1,2]天. 15104.乙值第[7]天,甲值第[5, 6]天,丁值第[3, 4]天,丙值第[1,2]天. 15105.乙值第[7]天,丙值第[5, 6]天,甲值第[3, 4]天,丁值第[1,2]天. 15106.乙值第[7]天,丙值第[5, 6]天,丁值第[3, 4]天,甲值第[1,2]天. 15107.乙值第[7]天,丁值第[5, 6]天,甲值第[3, 4]天,丙值第[1,2]天. 15108.乙值第[7]天,丁值第[5, 6]天,丙值第[3, 4]天,甲值第[1,2]天. 15109.丙值第[7]天,甲值第[5, 6]天,乙值第[3, 4]天,丁值第[1,2]天. 15110.丙值第[7]天,甲值第[5, 6]天,丁值第[3, 4]天,乙值第[1,2]天. 15111.丙值第[7]天,乙值第[5, 6]天,甲值第[3, 4]天,丁值第[1,2]天. 15112.丙值第[7]天,乙值第[5, 6]天,丁值第[3, 4]天,甲值第[1,2]天. 15113.丙值第[7]天,丁值第[5, 6]天,甲值第[3, 4]天,乙值第[1,2]天. 15114.丙值第[7]天,丁值第[5, 6]天,乙值第[3, 4]天,甲值第[1,2]天. 15115.丁值第[7]天,甲值第[5, 6]天,乙值第[3, 4]天,丙值第[1,2]天. 15116.丁值第[7]天,甲值第[5, 6]天,丙值第[3, 4]天,乙值第[1,2]天. 15117.丁值第[7]天,乙值第[5, 6]天,甲值第[3, 4]天,丙值第[1,2]天. 15118.丁值第[7]天,乙值第[5, 6]天,丙值第[3, 4]天,甲值第[1,2]天. 15119.丁值第[7]天,丙值第[5, 6]天,甲值第[3, 4]天,乙值第[1,2]天. 15120.丁值第[7]天,丙值第[5, 6]天,乙值第[3, 4]天,甲值第[1,2]天.
以上15120种方案用文本编辑器打开,随意找一行(去掉行号部分),在整体中查找,发现没有重复行,说明15120这个方案是对的。
END