说明
参考文章:数独高手必备的两个技巧(20年2月14日) 如果数字n在同一行或列中,只有两个空白格,在同一宫中两个空白格,并且其中有两个顶点的行列相同,则另外两个顶点的共同作用空白格,则不能有数字n
图片
算法代码
CalcEnum
在 绑定算法 中添加 属性
package com.suduku.calc.enums;
import com.suduku.calc.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
/**
* 功能描述: 算法枚举 <br/>
*
*/
@Getter
@AllArgsConstructor
public enum CalcEnum {
/***/
ONLY_NUM(OnlyNumCalc.class, "唯余法"),
ONLY_BOX(OnlyBoxCalc.class, "摒除法"),
GRID_XY(GridXYCalc.class, "单宫行列法"),
SU_DUI(SuDuiCalc.class, "数对法"),
SU_LIAN_J(SuLianDingCalc.class, "数链多宝鱼"), // 为了测试算法,将引入提前
X_WING(XwingCalc.class, "X-wing"),
Y_WING(YwingCalc.class, "Y-wing"),
XY_WING(XYwingCalc.class, "XY-wing"),
YX_WING(YXwingCalc.class, "YX-wing"),
XYZ_WING(XYZwingCalc.class, "XYZ-wing"),
SU_LIAN_V(SuLianVerticalCalc.class, "数链垂直"),
SU_LIAN_T(SuLianTrapezoidCalc.class, "数链梯形"),
;
private static final Map<Class<? extends AbstractCalc>, CalcEnum> CE_MAP = new HashMap<>(CalcEnum.values().length);
static {
for(CalcEnum ce : CalcEnum.values()) {
CE_MAP.put(ce.getClazz(), ce);
}
}
/**
* 功能描述: 通过类,获取枚举 <br/>
*
* @param clazz 类
* @return "com.suduku.calc.enums.CalcEnum"
*/
public static CalcEnum indexOf(Class<? extends AbstractCalc> clazz) {
return CE_MAP.get(clazz);
}
private final Class<? extends AbstractCalc> clazz;
private final String name;
}
SuLianDingCalc
package com.suduku.calc;
import com.suduku.entity.Box;
import com.suduku.util.SudoUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 数链-多宝鱼(如果数字n在同一行或列中,只有两个空白格,在同一宫中两个空白格,
* 并且其中有两个顶点的行列相同,
* 则另外两个顶点的共同作用空白格,则不能有数字n) <br/>
*
* 测试数据:DataConstant.OTHER_SU_LIAN_J_01
*/
public class SuLianDingCalc extends AbstractCalc {
/**
* 1. 遍历所有数字
* 2. 遍历所有宫,查找数字n只有两个空白格B1,B2
* 3. 遍历所有行或列,查找数字n只有两个空白格B3,B4(不在同宫中)
* 4. 校验B1B2,B3B4其中,有两个点处于同一列或行
* 5. 如果处于符合条件,则判断 另外两点的共同作用空白格不能有数字n
*/
@Override
Box solve() {
for(Integer n : Box.INIT_LIST) {
for (Map.Entry<Integer, List<Box>> gEntry : getGentries()) {
// 获取数字n出现的空白格
List<Box> b1b2List = gEntry.getValue().stream()
.filter(b -> b.isBlank() && b.getCList().contains(n))
.collect(Collectors.toList());
if(b1b2List.size() == 2) {
// 遍历行,获取B3,B4
Box clearBox = findAndClear(b1b2List, getXentries(), n, gEntry.getKey(), Box::getX, Box::getY);
if(clearBox != null) {
return clearBox;
}
clearBox = findAndClear(b1b2List, getYentries(), n, gEntry.getKey(), Box::getY, Box::getX);
if(clearBox != null) {
return clearBox;
}
}
}
}
return null;
}
/**
* 功能描述: 查找交叉点,并且清理共同作用空白格 <br/>
*
* @param b1b2List B1B2两个空白点
* @param entries 区域
* @param n 候选值
* @param g 当前宫下标
* @param getXFun 行坐标 区域是列,则互换
* @param getYFun 纵坐标 区域是列,则互换
* @return "com.suduku.entity.Box"
*/
private Box findAndClear(List<Box> b1b2List, Set<Map.Entry<Integer, List<Box>>> entries, Integer n, Integer g, Function<Box, Integer> getXFun, Function<Box, Integer> getYFun) {
for(Map.Entry<Integer, List<Box>> xEntry : entries) {
List<Box> b3b4List = xEntry.getValue().stream()
.filter(b -> b.isBlank() && b.getG() != g && b.getCList().contains(n))
.collect(Collectors.toList());
if(b3b4List.size() == 2 && SudoUtil.isTwoBox(b3b4List)) {
// 查找交叉点,并且清理
Box clearBox = findAndClear(b1b2List.get(0), b1b2List.get(1), b3b4List.get(0), b3b4List.get(1), n, getXFun, getYFun);
if(clearBox != null) {
return clearBox;
}
clearBox = findAndClear(b1b2List.get(1), b1b2List.get(0), b3b4List.get(0), b3b4List.get(1), n, getXFun, getYFun);
if(clearBox != null) {
return clearBox;
}
clearBox = findAndClear(b1b2List.get(0), b1b2List.get(1), b3b4List.get(1), b3b4List.get(0), n, getXFun, getYFun);
if(clearBox != null) {
return clearBox;
}
clearBox = findAndClear(b1b2List.get(1), b1b2List.get(0), b3b4List.get(1), b3b4List.get(0), n, getXFun, getYFun);
if(clearBox != null) {
return clearBox;
}
}
}
return null;
}
/**
* 功能描述: 查找交叉点,并且清理共同作用空白格 <br/>
*
* @param b1 同宫中第一个点与B3同行或列
* @param b2 同宫中第二个点
* @param b3 同行或列中第一个点,与B1同行或列
* @param b4 同行或列中第二个点
* @param n 候选值
* @param getXFun 行坐标
* @param getYFun 纵坐标
* @return "com.suduku.entity.Box"
*/
private Box findAndClear(Box b1, Box b2, Box b3, Box b4, Integer n, Function<Box, Integer> getXFun, Function<Box, Integer> getYFun) {
if(getYFun.apply(b1).equals(getYFun.apply(b3))) {
// 清理共同作用空白格
Box clearBox = getBoxByXY(getXFun.apply(b2), getYFun.apply(b4));
if(clearBox.isBlank() && clearBox.getCList().contains(n)) {
getListener().sendMsg("清除数字%d\n", n);
clearBox.removeCList(n);
return clearBox;
}
}
return null;
}
}
AbstractCalc
/**
* 功能描述: 方便使用 <br/>
*
* @return "java.util.Set<java.util.Map.Entry<java.lang.Integer,java.util.List<com.suduku.entity.Box>>>"
*/
protected Set<Map.Entry<Integer, List<Box>>> getXentries() {
return getXMap().entrySet();
}
/**
* 功能描述: 方便使用 <br/>
*
* @return "java.util.Set<java.util.Map.Entry<java.lang.Integer,java.util.List<com.suduku.entity.Box>>>"
*/
protected Set<Map.Entry<Integer, List<Box>>> getYentries() {
return getYMap().entrySet();
}
/**
* 功能描述: 方便使用 <br/>
*
* @return "java.util.Set<java.util.Map.Entry<java.lang.Integer,java.util.List<com.suduku.entity.Box>>>"
*/
protected Set<Map.Entry<Integer, List<Box>>> getGentries() {
return getGMap().entrySet();
}
测试数据
在 DataConstant 中添加
public static final String OTHER_SU_LIAN_J_01 = "001705049040390080900400700195674823000819050486532197029100000604900200010243960";
输出结果
尝试【数链多宝鱼 】
清除数字5
确认位置【行:3,列:9】 值为:【2】 候选值为:【】
0 (28 ) 6 ( ) 1 ( ) | 7 ( ) 0 (28 ) 5 ( ) | 3 ( ) 4 ( ) 9 ( ) |
0 (257 ) 4 ( ) 0 (27 ) | 3 ( ) 9 ( ) 1 ( ) | 0 (56 ) 8 ( ) 0 (56 ) |
9 ( ) 0 (35 ) 0 (38 ) | 4 ( ) 0 (68 ) 0 (68 ) | 7 ( ) 1 ( ){2}( ) |
1 ( ) 9 ( ) 5 ( ) | 6 ( ) 7 ( ) 4 ( ) | 8 ( ) 2 ( ) 3 ( ) |
0 (237 ) 0 (37 ) 0 (237 ) | 8 ( ) 1 ( ) 9 ( ) | 0 (46 ) 5 ( ) 0 (46 ) |
4 ( ) 8 ( ) 6 ( ) | 5 ( ) 3 ( ) 2 ( ) | 1 ( ) 9 ( ) 7 ( ) |
0 (3578 ) 2 ( ) 9 ( ) | 1 ( ) 0 (568 ) 0 (678 ) | 0 (45 ) 0 (37 ) 0 (458 ) |
6 ( ) 0 (357 ) 4 ( ) | 9 ( ) 0 (58 ) 0 (78 ) | 2 ( ) 0 (37 ) 1 ( ) |
0 (578 ) 1 ( ) 0 (78 ) | 2 ( ) 4 ( ) 3 ( ) | 9 ( ) 6 ( ) 0 (58 ) |
最终结果
8 ( ) 6 ( ) 1 ( ) | 7 ( ) 2 ( ) 5 ( ) | 3 ( ) 4 ( ) 9 ( ) |
2 ( ) 4 ( ) 7 ( ) | 3 ( ) 9 ( ) 1 ( ) | 5 ( ) 8 ( ) 6 ( ) |
9 ( ) 5 ( ) 3 ( ) | 4 ( ) 8 ( ) 6 ( ) | 7 ( ) 1 ( ) 2 ( ) |
1 ( ) 9 ( ) 5 ( ) | 6 ( ) 7 ( ) 4 ( ) | 8 ( ) 2 ( ) 3 ( ) |
3 ( ) 7 ( ) 2 ( ) | 8 ( ) 1 ( ) 9 ( ) | 6 ( ) 5 ( ) 4 ( ) |
4 ( ) 8 ( ) 6 ( ) | 5 ( ) 3 ( ) 2 ( ) | 1 ( ) 9 ( ) 7 ( ) |
5 ( ) 2 ( ) 9 ( ) | 1 ( ) 6 ( ) 7 ( ) | 4 ( ) 3 ( ) 8 ( ) |
6 ( ) 3 ( ) 4 ( ) | 9 ( ) 5 ( ) 8 ( ) | 2 ( ) 7 ( ) 1 ( ) |
7 ( ) 1 ( ) 8 ( ) | 2 ( ) 4 ( ) 3 ( ) | 9 ( ) 6 ( ){5}( ) |
总结
在写代码的过程中,研究案例,并且思考相关变化。