我是Constraint Programming的新手,我正在尝试解决一个问题,从二维数组中,由数字组成,我需要尽量少量的子数组(2D),覆盖尽可能多的尽可能使用原始2D数组,遵守以下规则:
例如,对于以下矩阵:
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()的情况下实现相同的约束,那么我会尝试一下,但我无法想到任何其他方式。
谢谢!
答案 0 :(得分:1)
在解算器内部基于IntVar从数组中获取值的方法是使用MakeElement()
函数,在这种情况下使用2d version函数。
通过这种方式,您可以从矩阵中获取特定值,但不能获得基于两个IntVar的范围(例如矩形的x - dx)。要完成范围部分,您可以使用循环和ConditionalExpression()
来确定指定的值是否在范围内。
例如,在1d数组中,为了从data
获取元素,位置x
到x + 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。我不知道这是否是实现它的最佳方法,但这是我能想到的最好方法,因为我无法在网上找到类似的东西。