问题描述: 有 三个 盒子,每个盒子内有若干个球,两个人轮流取,每人每次只能从一个盒子内取至少一个球,取到最后一个球的人输。
比如3,4,5的话,先手通过特定的步骤能够获胜。
问题分析:没有找到必胜的潜在规律,因此,编写遍历所有情况,以判断先手是否必胜。这是一个递归调用的问题,因此需要设置递归的结束条件。
通过树汇总所有的情况,然后将可以胜利的每个步骤都打印出来。
具体的代码如下:
package game; import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class howWin { private int BOX_NUM = 0; private int[] BoxList = null; private treeNode travelTree; private List<treeNode> YesNode = new ArrayList<treeNode>(); private List<treeNode> otherFixedFailState = new ArrayList<treeNode>(); public class treeNode { public List<treeNode> childs; public String nodeInfo; public treeNode father; public treeNode(String info){ nodeInfo = info; childs = new ArrayList(); childs.clear(); } public void addNode(treeNode tree) { childs.add(tree); tree.father = this; } } public howWin(){ } public void setBoxBallNum(int[] boxSum){ BoxList = boxSum; BOX_NUM = BoxList.length; travelTree = new treeNode("Root" + genListInfo(BoxList)); } // 必然可以获胜的情形 public boolean fixWin(int[] middleState){ int countNoEmptyBox = 0; List<Integer> noEmptyIndex = new ArrayList<Integer>(); for (int i = 0; i < BOX_NUM; i++){ if (middleState[i] > 0) { countNoEmptyBox += 1; noEmptyIndex.add(i); } } // 只剩一个盒子内有球,并且球的数量不少于2个 if (countNoEmptyBox == 1 && middleState[noEmptyIndex.get(0).intValue()] >= 2) return true; // 两个盒子中有球,并且一个盒子中球的数量是1 if (countNoEmptyBox == 2 && (middleState[noEmptyIndex.get(0).intValue()] == 1 || middleState[noEmptyIndex.get(1).intValue()] == 1)){ return true; } return false; } public boolean afterOtherActionCanWin(int[] middleState, treeNode travel){ if (fixWin(middleState)) { treeNode cur = new treeNode("NO"); travel.addNode(cur); return false; } for (int i = 0; i < BOX_NUM; i++){ for (int j = 1; j <= middleState[i]; j++){ int[] tmp = middleState.clone(); tmp[i] -= j; treeNode cur = new treeNode("B|"+genListInfo(tmp)); travel.addNode(cur); if (!canWin(tmp, cur)){ return false; } } } return true; } public boolean canWin(int[] middleState, treeNode travel){ if (fixWin(middleState)) { treeNode cur = new treeNode("YES"); travel.addNode(cur); YesNode.add(cur); return true; } for (int i = 0; i < BOX_NUM; i++){ for (int j = 1; j <= middleState[i]; j++){ int[] tmp = middleState.clone(); tmp[i] -= j; treeNode cur = new treeNode("A|" + genListInfo(tmp)); travel.addNode(cur); if (afterOtherActionCanWin(tmp, cur)){ otherFixedFailState.add(cur); return true; } } } return false; } private String genListInfo(int[] midState){ String Info = ""; for (int i = 0; i < midState.length; i++) Info += midState[i] + " "; return Info; } public boolean caculateResult(){ if (canWin(BoxList, travelTree)) { System.out.println("YES: the first one sure win"); return true; } System.out.println("NOT SURE: the first win"); return false; } public void dept(treeNode cur, List<treeNode> mustShow, int depts){ if (depts % 2 == 1 && !mustShow.contains(cur)){ return; } System.out.print("["+depts+"]"); for (int i = 0; i < depts; i++) { System.out.print("----"); } System.out.println(cur.nodeInfo); for (int i = 0; i < cur.childs.size(); i++) { dept(cur.childs.get(i), mustShow, depts+1); } } public void printWinSteps(){ List<treeNode> mustShowNodes = new ArrayList<>(); for (int i = 0; i < YesNode.size(); i++) { treeNode cur = YesNode.get(i); while (cur != null){ mustShowNodes.add(cur); cur = cur.father; } } dept(travelTree, otherFixedFailState, 0); } public static int[] scanBoxBallInfo(){ Scanner sb = new Scanner(System.in); System.out.print("退出请输入q. \n 输入每个盒子内的小球数目,\n如三个盒子,每个盒子放3、4、5个球,则输入为:3,4,5\n"); String name = sb.nextLine(); if (name.contains("q") || name.contains("Q")) { System.exit(0); } String[] ballSum = name.split(","); int[] ballSumInt = new int[ballSum.length]; boolean parseOk = true; for (int i = 0; i < ballSum.length; i++){ try { ballSumInt[i] = Integer.parseInt(ballSum[i]); }catch (Exception e){ e.printStackTrace(); parseOk = false; } if (ballSumInt[i] < 0) { System.out.println("输入有异常,请重新输入"); parseOk = false; break; } } if (!parseOk) return null; return ballSumInt; } public static void main(String[] args){ while(true) { int[] ballSum = scanBoxBallInfo(); if (ballSum == null) continue; howWin cur = new howWin(); cur.setBoxBallNum(ballSum); if (cur.caculateResult()) cur.printWinSteps(); } } }
代码分析:
1. 此代码实现了基本的遍历内容,但是有很大的优化空间,比如判断先手的胜利的步骤,可以汇总从拿取最多球的开始遍历,找到正确步骤的速度会快很多。
2. 该代码打印先手胜利的一种方法,但是不是唯一方法。
请大家批评指正!