说明
参考文章:数独高手必备:把数串法推广到数块法(20年6月2日) 如果某列中,数字n只有3个空白格B1,B2,B3,并且其中有两个在同一个宫内,另外一个在不同宫内,同时存在另外一个列中,数字n只有两个空白格B4,B5,且不在同一宫内,并且不在B1B2B3宫内。并且满足B1,B2,B3与B4,B5能够形成矩形。则在有两个空白格的宫内与矩形边共同作用的区域,不能有数字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, "数对法"),
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_K(SuLianKuaiCalc.class, "数链数块"),
SU_LIAN_V(SuLianVerticalCalc.class, "数链垂直"),
SU_LIAN_T(SuLianTrapezoidCalc.class, "数链梯形"),
SU_LIAN_J(SuLianDingCalc.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;
}
SuLianKuaiCalc
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只有3个空白格B1,B2,B3,并且其中有两个在同一个宫内,另外一个在不同宫内,
* 同时存在另外一个列中,数字n只有两个空白格B4,B5,且不在同一宫内,并且不在B1B2B3宫内。
* 并且满足B1,B2,B3与B4,B5能够形成矩形。
* 则在有两个空白格的宫内与矩形边共同作用的区域,不能有数字n) <br/>
*
* 测试数据:DataConstant.OTHER_SU_LIAN_K_01
* 测试数据:SudoUtil.transpose(DataConstant.OTHER_SU_LIAN_K_01)
*/
public class SuLianKuaiCalc extends AbstractCalc {
/**
* 1. 遍历所有数字
* 2. 遍历所有行,找出数字n的空白格,并且只有3个还满足 B1B2|B3 或 B1|B2B3的分布条件
* 3. 再次遍历所有行,找出数字n的空白格,且只有2个,还满足跟第一列不在同一宫,且两个空白格也不在同一宫
* 4. 校验是否其中有4个点形成矩形
* 5. 剔除单元格
*/
@Override
Box solve() {
for(Integer n : Box.INIT_LIST) {
Box clearBox = findAndClear(getYentries(), n, Box::getX);
if (clearBox != null) {
return clearBox;
}
clearBox = findAndClear(getXentries(), n, Box::getY);
if (clearBox != null) {
return clearBox;
}
}
return null;
}
/**
* 功能描述: 查找并清理 <br/>
*
* @param entries 遍历的区域,行或列
* @param n 候选值
* @param getFun 方法
* @return "com.suduku.entity.Box"
*/
private Box findAndClear(Set<Map.Entry<Integer, List<Box>>> entries, Integer n, Function<Box, Integer> getFun) {
// 遍历所有列,获取数字n所在的列只有3个空白格,并且其中有两个空白格在同一宫内
for (Map.Entry<Integer, List<Box>> l1Entry : entries) {
List<Box> b1b2b3List = l1Entry.getValue().stream()
.filter(b -> b.isBlank() && b.getCList().contains(n))
.collect(Collectors.toList());
// 判断是否有两个空白格在同一宫内,另外一个空白格不在该宫内
if(SudoUtil.isTwoBoxAndOne(b1b2b3List)) {
for (Map.Entry<Integer, List<Box>> l2Entry : entries) {
// 再查找出只有两个空白格的列
List<Box> b4b5List = l2Entry.getValue().stream()
.filter(b -> b.isBlank() && b.getCList().contains(n))
.collect(Collectors.toList());
if (SudoUtil.isTwoBox(b4b5List) && b1b2b3List.get(0).getG() != b4b5List.get(0).getG()) {
// 形成矩形,并清理单元格
Box clearBox = findAndClear(b1b2b3List.get(0), b1b2b3List.get(1), b1b2b3List.get(2), b4b5List, n, getFun);
if (clearBox != null) {
return clearBox;
}
clearBox = findAndClear(b1b2b3List.get(1), b1b2b3List.get(0), b1b2b3List.get(2), b4b5List, n, getFun);
if (clearBox != null) {
return clearBox;
}
clearBox = findAndClear(b1b2b3List.get(2), b1b2b3List.get(0), b1b2b3List.get(1), b4b5List, n, getFun);
if (clearBox != null) {
return clearBox;
}
}
}
}
}
return null;
}
/**
* 功能描述: 发现矩形,并且清理单元格 <br/>
*
* @param b1 不在矩形内的点
* @param b2 矩形第一个端点
* @param b3 矩形第二个端点
* @param b4b5List 矩形端点
* @param n 候选值
* @return "com.suduku.entity.Box"
*/
private Box findAndClear(Box b1, Box b2, Box b3, List<Box> b4b5List, Integer n, Function<Box, Integer> getFun) {
// 判断是否形成矩形
if(getFun.apply(b2).equals(getFun.apply(b4b5List.get(0))) && getFun.apply(b3).equals(getFun.apply(b4b5List.get(1)))) {
// 获取宫内数据
List<Box> clearList = getGList(b1).stream()
.filter(b -> b.isBlank()
&& b.getX() == (b1.getG() == b2.getG() ? getFun.apply(b2) : getFun.apply(b3))
&& b.getI() != b2.getI() && b.getI() != b3.getI()
&& b.getCList().contains(n))
.collect(Collectors.toList());
for(Box box : clearList) {
box.removeCList(n);
change(box, SudoUtil.getList(b4b5List, b1, b2, b3), n);
return box;
}
}
return null;
}
}
SudoUtil
/**
* 功能描述: 是否两个单元格在一个宫内,并且另外一个不在该宫内 <br/>
*
* @param list 列表
* @return "boolean"
*/
public static boolean isTwoBoxAndOne(List<Box> list) {
// B1B2 | B3
return list.size() == 3
&& ((list.get(0).getG() == list.get(1).getG() && list.get(0).getG() != list.get(2).getG())
// B1 | B2B3
|| (list.get(0).getG() != list.get(1).getG() && list.get(1).getG() == list.get(2).getG()));
}
测试数据
在 DataConstant 中添加
public static final String OTHER_SU_LIAN_K_01 = "020089103398001042710032089806290314109346820243018096932800461567124938481963200";
输出结果
尝试【数链数块 】
Box{v=0, i=15, x=1, y=6, cList=[5, 6, 7]}
Box{v=0, i=51, x=5, y=6, cList=[5, 7]}
Box{v=0, i=3, x=0, y=3, cList=[4, 5, 7]}
Box{v=0, i=12, x=1, y=3, cList=[5, 6, 7]}
Box{v=0, i=48, x=5, y=3, cList=[5, 7]}
清除单元格【Box{v=5, i=13, x=1, y=4, cList=[]}】的候选值[7]
6 ( ) 2 ( ) 0 (45 ) | [0](457 ) 8 ( ) 9 ( ) | 1 ( ) 0 (57 ) 3 ( ) |
3 ( ) 9 ( ) 8 ( ) | [0](567 ){5}( ) 1 ( ) | [0](567 ) 4 ( ) 2 ( ) |
7 ( ) 1 ( ) 0 (45 ) | 0 (456 ) 3 ( ) 2 ( ) | 0 (56 ) 8 ( ) 9 ( ) |
8 ( ) 0 (57 ) 6 ( ) | 2 ( ) 9 ( ) 0 (57 ) | 3 ( ) 1 ( ) 4 ( ) |
1 ( ) 0 (57 ) 9 ( ) | 3 ( ) 4 ( ) 6 ( ) | 8 ( ) 2 ( ) 0 (57 ) |
2 ( ) 4 ( ) 3 ( ) | [0](57 ) 1 ( ) 8 ( ) | [0](57 ) 9 ( ) 6 ( ) |
9 ( ) 3 ( ) 2 ( ) | 8 ( ) 0 (57 ) 0 (57 ) | 4 ( ) 6 ( ) 1 ( ) |
5 ( ) 6 ( ) 7 ( ) | 1 ( ) 2 ( ) 4 ( ) | 9 ( ) 3 ( ) 8 ( ) |
4 ( ) 8 ( ) 1 ( ) | 9 ( ) 6 ( ) 3 ( ) | 2 ( ) 0 (57 ) 0 (57 ) |
最终结果
6 ( ) 2 ( ) 5 ( ) | 4 ( ) 8 ( ) 9 ( ) | 1 ( ) 7 ( ) 3 ( ) |
3 ( ) 9 ( ) 8 ( ) | 7 ( ) 5 ( ) 1 ( ) | 6 ( ) 4 ( ) 2 ( ) |
7 ( ) 1 ( ) 4 ( ) | 6 ( ) 3 ( ) 2 ( ) | 5 ( ) 8 ( ) 9 ( ) |
8 ( ) 5 ( ) 6 ( ) | 2 ( ) 9 ( ) 7 ( ) | 3 ( ) 1 ( ) 4 ( ) |
1 ( ) 7 ( ) 9 ( ) | 3 ( ) 4 ( ) 6 ( ) | 8 ( ) 2 ( ) 5 ( ) |
2 ( ) 4 ( ) 3 ( ) | 5 ( ) 1 ( ) 8 ( ) | 7 ( ) 9 ( ) 6 ( ) |
9 ( ) 3 ( ) 2 ( ) | 8 ( ) 7 ( ) 5 ( ) | 4 ( ) 6 ( ) 1 ( ) |
5 ( ) 6 ( ) 7 ( ) | 1 ( ) 2 ( ) 4 ( ) | 9 ( ) 3 ( ) 8 ( ) |
4 ( ) 8 ( ) 1 ( ) | 9 ( ) 6 ( ) 3 ( ) | 2 ( ) 5 ( ){7}( ) |
============数独解题完成,尝试次数为:29============
总结
由于之前编写的代码,没有考虑输出直观,所以重构了输出展示的方式。请先下载最新代码查看