获取动态子矩阵并应用约束

时间:2017-01-09 17:42:14

标签: constraint-programming or-tools

我是Constraint Programming的新手,我正在尝试解决一个问题,从二维数组中,由数字组成,我需要尽量少量的子数组(2D),覆盖尽可能多的尽可能使用原始2D数组,遵守以下规则:

  • 每个子数组必须是原始
  • 的矩形部分
  • 每个子数组中的数字总和不得超过特定数字
  • 每个子数组中必须至少包含2个数字

例如,对于以下矩阵:

3 5 1 4
5 1 2 8
0 8 1 3
8 3 2 1

最大总和为10,解决方案是:

 3 -not picked 
{ 5 1 4 }
{ 5 1 }
{ 2 8 }
{ 0 8 }
{ 1 3 
  2 1 }
 8 -not picked

现在我正在使用diffn()等效的or-tools(MakeNonOverlappingBoxesConstraint())来创建将覆盖原始数组的矩形。

我的问题是如何获取diffn()创建的矩形,并根据每个矩阵的位置和大小拆分原始矩阵,因此我可以应用 Sum 约束。

如果有另一种方法可以在不使用diffn()的情况下实现相同的约束,那么我会尝试一下,但我无法想到任何其他方式。

谢谢!

1 个答案:

答案 0 :(得分:1)

在解算器内部基于IntVar从数组中获取值的方法是使用MakeElement()函数,在这种情况下使用2d version函数。

通过这种方式,您可以从矩阵中获取特定值,但不能获得基于两个IntVar的范围(例如矩形的x - dx)。要完成范围部分,您可以使用循环和ConditionalExpression()来确定指定的值是否在范围内。

例如,在1d数组中,为了从data获取元素,位置xx + dx将如下所示

int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IntVar x = solver.MakeIntVar(0, data.Length - 1);
IntVar dx = solver.MakeIntVar(1, data.Length);

solver.Add(x + dx <= data.Length);

IntVarVector range = new IntVarVector();
for (int i = 0; i < dx.Max(); i++)
{
    range.Add(solver.MakeConditionalExpression((x + i < x + dx).Var() , solver.MakeElement(data, (x + i).Var()), 0).Var());
}

solver.Add(range.ToArray().Sum() <= 10);

在2d数组的情况下(如问题中那样),您只需遍历两个维度。唯一的区别是MakeElement()的第2版接受IndexEvaluator2项(C#中的LongLongToLong),因此您必须创建自己的继承LongLongToLong的类并覆盖{ {1}}功能。

Run()

这个类的唯一问题是它可以从数组中请求一个值,所以我们必须自己使用class DataValues: LongLongToLong { private int[,] _data; private int _rows; private int _cols; public DataValues(int[,] data, int rows, int cols) { _rows = rows; _cols = cols; _data = data; } public override long Run(long arg0, long arg1) { if (arg0 >= _rows || arg1 >= _cols) return 0; return _data[arg0, arg1]; } } 来处理它。

P.S。我不知道这是否是实现它的最佳方法,但这是我能想到的最好方法,因为我无法在网上找到类似的东西。