【数学解法】
位居首尾,编号为1和10的两盏灯是不能关的,这俩不考虑。
编号2-9的8盏灯关三盏,方案总数是C83,但这里有连续关闭的情况和题意抵触,再做下去不好做。
反过来想以下,把7盏打开的灯放好,把三盏关闭的灯往打开的灯中间插,就不存在连续的问题。
7盏灯外侧不能插,只能插在中间的六个空,这六个空就是三盏关闭的灯的插入位置。
6个空里选三个空插关闭的灯,方案是C63=6*5*4/3/2/1=20种。
【代码解法】
做一个从1到8的数组,用组合器进行8选三,以不连续为过滤条件去掉连续关闭的情况,剩下的就是符合条件的关灯方案。
主类Roadlamp代码:

package test230427;
import java.util.List;
import test230425.Combination;
/**
* 马路上有编号为1,2,3,4,......,10的十盏路灯,为省钱准备关掉其中的三盏,但又不能关掉相邻的两盏或三盏,而且两端的灯也不能关,求满足条件的关灯方案?
*/
public class Roadlamp {
public static void main(String[] args) {
final int[] arr= {1,2,3,4,5,6,7,8};// 分别对应第2到第8盏灯
Combination c=new Combination(arr,3);// 进行8选3
List<List<Integer>> results=c.getResults();
int idx=0;
for(List<Integer> res:results) {
// 过滤掉连续的灯
boolean continuous=false;
for(int i=0;i<res.size();i++) {
for(int j=i+1;j<res.size();j++) {
if((res.get(i)+1)==res.get(j)) {
continuous=true;
break;
}
}
}
if(continuous) {
continue;
}
// 打开的灯
String[] days= {"➀","➁","➂","➃","➄","➅","➆","➇","➈","➉"};
// 关闭的灯
String[] nights= {"➊","➋","➌","➍","➎","➏","➐","➑","➒","➓"};
// 关掉三盏灯
for(int i:res) {
days[i]=nights[i];
}
System.out.println(String.format("%02d", ++idx) +"."+String.join(",", days));
}
}
}

辅助类Combination代码:

package test230425;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 数学中排列组合中的组合器实现
*
*/
public 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);
}
}
}

【运行结果即20种关灯方案】
01.➀,➋,➂,➍,➄,➏,➆,➇,➈,➉
02.➀,➋,➂,➍,➄,➅,➐,➇,➈,➉
03.➀,➋,➂,➍,➄,➅,➆,➑,➈,➉
04.➀,➋,➂,➍,➄,➅,➆,➇,➒,➉
05.➀,➋,➂,➃,➎,➅,➐,➇,➈,➉
06.➀,➋,➂,➃,➎,➅,➆,➑,➈,➉
07.➀,➋,➂,➃,➎,➅,➆,➇,➒,➉
08.➀,➋,➂,➃,➄,➏,➆,➑,➈,➉
09.➀,➋,➂,➃,➄,➏,➆,➇,➒,➉
10.➀,➋,➂,➃,➄,➅,➐,➇,➒,➉
11.➀,➁,➌,➃,➎,➅,➐,➇,➈,➉
12.➀,➁,➌,➃,➎,➅,➆,➑,➈,➉
13.➀,➁,➌,➃,➎,➅,➆,➇,➒,➉
14.➀,➁,➌,➃,➄,➏,➆,➑,➈,➉
15.➀,➁,➌,➃,➄,➏,➆,➇,➒,➉
16.➀,➁,➌,➃,➄,➅,➐,➇,➒,➉
17.➀,➁,➂,➍,➄,➏,➆,➑,➈,➉
18.➀,➁,➂,➍,➄,➏,➆,➇,➒,➉
19.➀,➁,➂,➍,➄,➅,➐,➇,➒,➉
20.➀,➁,➂,➃,➎,➅,➐,➇,➒,➉
数学解法和程序解法结果一致,可以相互印证。
END
文章介绍了如何解决一个数学问题,即在1到10编号的灯中关闭3盏,但不能关闭相邻或两端的灯。提供了数学组合和编程(Java)两种解法。数学解法利用组合公式排除连续关闭的情况,得到20种方案。编程解法通过创建组合器类进行8选3并过滤掉连续关闭的组合,最终同样得到20种符合条件的关灯方案。

798

被折叠的 条评论
为什么被折叠?



