You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MiniZinc中建模Hitori谜题的连通性约束?

Hitori谜题连通性约束的MiniZinc建模问题

Hitori谜题是n×n网格,填充1到n的数字,部分数字在行/列重复。目标是覆盖部分重复数字,需满足三个核心条件:

  • 任意行、列无重复数字;
  • 被覆盖单元格不能垂直或水平相邻;
  • 未覆盖单元格需通过垂直/水平方向连通(无孤立区域)。

现有MiniZinc模型运行给定数据集时,会生成未覆盖单元格不连通的错误解。当前仅实现了避免单单元格孤岛的初步连通性约束,移除该约束会生成33个含孤立单元格的解。曾考虑用n²×n²马尔可夫转移矩阵建模,但认为这种方式低效且不优雅,现寻求在MiniZinc中正确建模未覆盖单元格连通性约束的方法。


现有MiniZinc模型代码(hitori.mzn)

% hitori.mzn  

% Given square a grid of sidelength n
% The grid is filled with numbers 1..n, some of them repeated in rows and/or columns
% Goal: Cover some of the repeated numbers so that
% i)    there are no duplicates in any row of column
% ii)   no covered square is vertically of horizontally adjacent to another coverde square
% iii)  the uncovered blocks are connected 

% data instance defined in *.dzn 
int: nsize ;
array[SDX,SDX] of SDX: hitoritabl ;

set of int: SDX = 1..nsize ;
set of int: SDX0 = 0..nsize ;  
set of int: SDX2 = 2..nsize ;

% primary unknowns, 0 or 1 whether whether a square is covered or open
array[SDX,SDX] of var 0..1 : hitorimask ;

% modelling variable to display the solution, 0 is used to indicate a covered cell, ie a blacked cell
array[SDX,SDX] of var SDX0: hitorisoln ;
constraint forall( ir in SDX, jc in SDX ) ( hitorisoln[ir,jc] = hitorimask[ir,jc] * hitoritabl[ir,jc] ) ;

% model constraints

% use primary variables to construct quantity on which nonrepeat constraint can be imposed
include "alldifferent_except_0.mzn" ; 
constraint forall ( ir in SDX) ( alldifferent_except_0 ( [ hitorisoln[ir,jc]| jc in SDX ] ) ) ;  % no repeats in a row
constraint forall ( jc in SDX) ( alldifferent_except_0 ( [ hitorisoln[ir,jc]| ir in SDX ] ) ) ;  % no repeats in a column

% impose condition on non-adjacent masked cells
% no black square directly below anothe black square
constraint forall (ir in SDX2, jc in SDX) ( 0 < hitorimask[ir-1,jc] + hitorimask[ir,jc] ) ;
% no black square directly to the right of anothe black square
constraint forall (ir in SDX, jc in SDX2) ( 0 < hitorimask[ir,jc-1] + hitorimask[ir,jc] ) ;

% this constraint simply excludes islands of singleton open cells
constraint forall (ir in SDX, jc in SDX) 
           ( if hitorimask[ir,jc]=1 then 
                ( if ir > 1     then hitorimask[ir-1,jc  ] else 0 endif 
                + if jc < nsize then hitorimask[ir  ,jc+1] else 0 endif
                + if ir < nsize then hitorimask[ir+1,jc  ] else 0 endif
                + if jc > 1     then hitorimask[ir  ,jc-1] else 0 endif
                 > 0 
                )
             endif ) ;

%
% still need a condition of connectedness of open squares
%

solve satisfy ;

output ["\nhitorimask           hitorisoln \n" ] ;
output [ 
         if jc <= nsize then "" ++ show_int(3, hitorimask[ir,jc] ) else "" endif
      ++ if jc = nsize then "   " else "" endif 
      ++ if nsize < jc /\ jc <= 2*nsize 
         then if fix(hitorisoln[ir,jc-nsize])>0 
              then show_int(3, hitorisoln[ir,jc-nsize] )  
              else "  ." 
              endif  
         else "" 
         endif 
      ++ if jc = 2*nsize then "\n" else "" endif
      | ir in SDX, jc in 1..2*nsize ] ;

数据集代码(hitori.dzn)

% hitori.dzn
nsize = 5 ;   % size of square grid

hitoritabl = [|
4, 5, 4, 4, 3 |
4, 2, 5, 1, 3 |
5, 1, 1, 3, 4 |
5, 4, 2, 5, 2 |
2, 1, 4, 3, 1 |] ;

解决方案:建模未覆盖单元格的全局连通性

在MiniZinc中实现全局连通性,最实用且高效的方式是基于可达性的传递闭包建模,具体实现步骤如下:

1. 添加可达性变量与根节点约束

引入二维布尔数组reachable表示单元格是否从某个未覆盖的根节点可达,同时定义根节点变量确保存在起始连通点:

array[SDX, SDX] of var bool: reachable;
var SDX: root_i;
var SDX: root_j;

% 选择一个未覆盖单元格作为连通起点
constraint hitorimask[root_i, root_j] = 1;
constraint reachable[root_i, root_j] = true;

2. 约束可达性的传递关系

如果某个单元格可达且未被覆盖,那么它的相邻未覆盖单元格也必须可达:

constraint forall(i in SDX, j in SDX) (
    if reachable[i,j] then
        % 上邻单元格连通传递
        (i > 1 /\ hitorimask[i-1,j] = 1) -> reachable[i-1,j]
        /\
        % 下邻单元格连通传递
        (i < nsize /\ hitorimask[i+1,j] = 1) -> reachable[i+1,j]
        /\
        % 左邻单元格连通传递
        (j > 1 /\ hitorimask[i,j-1] = 1) -> reachable[i,j-1]
        /\
        % 右邻单元格连通传递
        (j < nsize /\ hitorimask[i,j+1] = 1) -> reachable[i,j+1]
    endif
);

3. 强制所有未覆盖单元格可达

确保所有未被覆盖的单元格都能从根节点到达:

constraint forall(i in SDX, j in SDX) (
    hitorimask[i,j] = 1 -> reachable[i,j]
);

修改后的完整模型

将上述代码添加到原模型中% still need a condition of connectedness of open squares注释的位置即可。原有的单单元格孤岛约束可以保留,作为前置剪枝条件提升求解效率。


内容的提问来源于stack exchange,提问作者donman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:44:52