J
JohnsonGuo
Unregistered / Unconfirmed
GUEST, unregistred user!
使用整数线性规划故然可以,但是不是有点杀鸡用牛刀的感觉呢?其实用宽度搜索足矣
interface
uses
SysUtils, Classes, Dialogs;
const
n = 2;
//材料总数
Len = 60;
//料坯长度
MatLen: array [1..n] of Integer = (55, 5);
//各种材料长度(按大到小)
MatQty: array [1..n] of Integer = (12, 12);
//各种材料需要的数量
type
//定义节点记录
PNode = ^TNode;
TNode = record
Count: Integer;
//节点所在层,等于所用料坯数量
MatCnt: array [1..n] of Integer;
//到当前节点为至,各种材料的数量
PrjIndex: Integer;
//当前节点使用的开料方案
Parent: PNode;
//当前节点的父节点
end;
//节点队列,用于宽度搜索
TNodeList = class(TList)
private
function GetItem(Index: Integer): PNode;
protected
procedure Notify(Ptr: Pointer;
Action: TListNotification);
override;
public
property Items[Index: Integer]: PNode read GetItem;
default;
end;
var
MatPrj: array of array [1..n] of Integer;
//记录各开料方案
MatPrjCnt: Integer;
//开料方案数
procedure CalcProject;
procedure OptimalProject;
implementation
//计算开料方案
procedure CalcProject;
var
Prj: array [1..n] of Integer;
procedure CalcProjectSub(Len, Index: Integer);
//Len当前剩余料坯长度,用剩余料坯开第Index总材料
var
RemLen, Max, i: Integer;
begin
//递归退出条件
if (Len = 0) or (Index > n) then
begin
Inc(MatPrjCnt);
SetLength(MatPrj, MatPrjCnt);
Move(Prj[1], MatPrj[MatPrjCnt - 1][1], SizeOf(Prj));
Exit;
end;
//计算剩余料坯长度可以最多开多少第Index种材料
Max := Len div MatLen[Index];
if Index < n then
//当前不是开最后一种材料,则分别开0至Max个,保留部分料坯长度给后继材料
for i := Maxdo
wnto 0do
begin
Prj[Index] := i;
RemLen := Len - i * MatLen[Index];
//料坯长度为0,其后的材料不能再开,所以设0
if RemLen = 0 then
FillChar(Prj[Index + 1], (n - Index) * SizeOf(Integer), 0);
CalcProjectSub(RemLen, Index + 1);
end
else
begin
//最后一种材料了,设置最大的开料数量即可
Prj[Index] := Max;
CalcProjectSub(0, Index + 1);
end;
end;
begin
//初始开料方案
SetLength(MatPrj, 0);
MatPrjCnt := 0;
//由于材料的数量n不定,故用递归方案计算各开料方案
CalcProjectSub(Len, 1);
end;
function CheckSuccess(Node: PNode): Boolean;
//检查当前的方案是否已经满足MatQty的要求
var
i: Integer;
begin
Result := True;
for i := 1 to ndo
if Node.MatCnt < MatQty then
begin
//只要其中一个小于MatQty,则为不满足
Result := False;
Break;
end;
end;
procedure ShowSolution(Node: PNode);
//显示第一个解决方案
var
s: String;
i: Integer;
begin
s := Format('所需要的最少料坯数量: %d'#13#10'开料方案如下:'#13#10,
[Node.Count]);
while Assigned(Node)do
begin
for i := 1 to ndo
AppendStr(s, IntToStr(MatPrj[Node.PrjIndex, i]) + ' ');
AppendStr(s, #13#10);
Node := Node.Parent;
end;
ShowMessage(s);
end;
procedure OptimalProject;
//求解最优方案
var
List: TNodeList;
Node, NewNode: PNode;
i, j, p: Integer;
begin
//建立队列
List := TNodeList.Create;
try
//初始化队列中的元素,即把各种方案入队
for i := 0 to MatPrjCnt - 1do
begin
New(Node);
with Node^do
begin
Count := 1;
Move(MatPrj[1], MatCnt[1], SizeOf(MatCnt));
PrjIndex := i;
Parent := nil;
end;
List.Add(Node);
//检查是否某一种开料方案即可满足要求
if CheckSuccess(Node) then
begin
ShowSolution(Node);
Exit;
end;
end;
p := 0;
while p < List.Countdo
begin
//从列队中取出第一个节点
Node := List[p];
//在第一个节点基础上,在分别开一条料坯并入队列
for i := Node.PrjIndex to MatPrjCnt - 1do
begin
New(NewNode);
with NewNode^do
begin
Count := Node.Count + 1;
Move(Node.MatCnt[1], MatCnt[1], SizeOf(MatCnt));
for j := 1 to ndo
Inc(MatCnt[j], MatPrj[i, j]);
PrjIndex := i;
Parent := Node;
end;
List.Add(NewNode);
//检测是否开了这一条料坯后,可以达到要求
if CheckSuccess(NewNode) then
begin
ShowSolution(NewNode);
Exit;
end;
end;
Inc(p);
end;
finally
List.Free;
end;
end;
{ TNodeList }
function TNodeList.GetItem(Index: Integer): PNode;
begin
Result := inherited Items[Index];
end;
procedure TNodeList.Notify(Ptr: Pointer;
Action: TListNotification);
begin
if Action = lnDeleted then
Dispose(PNode(Ptr));
inherited;
end;
调用方法
begin
CalcProject;
OptimalProject;
end;
如果要考虑料头或者料间,或者需要多种解决方案,适当修改即可.
interface
uses
SysUtils, Classes, Dialogs;
const
n = 2;
//材料总数
Len = 60;
//料坯长度
MatLen: array [1..n] of Integer = (55, 5);
//各种材料长度(按大到小)
MatQty: array [1..n] of Integer = (12, 12);
//各种材料需要的数量
type
//定义节点记录
PNode = ^TNode;
TNode = record
Count: Integer;
//节点所在层,等于所用料坯数量
MatCnt: array [1..n] of Integer;
//到当前节点为至,各种材料的数量
PrjIndex: Integer;
//当前节点使用的开料方案
Parent: PNode;
//当前节点的父节点
end;
//节点队列,用于宽度搜索
TNodeList = class(TList)
private
function GetItem(Index: Integer): PNode;
protected
procedure Notify(Ptr: Pointer;
Action: TListNotification);
override;
public
property Items[Index: Integer]: PNode read GetItem;
default;
end;
var
MatPrj: array of array [1..n] of Integer;
//记录各开料方案
MatPrjCnt: Integer;
//开料方案数
procedure CalcProject;
procedure OptimalProject;
implementation
//计算开料方案
procedure CalcProject;
var
Prj: array [1..n] of Integer;
procedure CalcProjectSub(Len, Index: Integer);
//Len当前剩余料坯长度,用剩余料坯开第Index总材料
var
RemLen, Max, i: Integer;
begin
//递归退出条件
if (Len = 0) or (Index > n) then
begin
Inc(MatPrjCnt);
SetLength(MatPrj, MatPrjCnt);
Move(Prj[1], MatPrj[MatPrjCnt - 1][1], SizeOf(Prj));
Exit;
end;
//计算剩余料坯长度可以最多开多少第Index种材料
Max := Len div MatLen[Index];
if Index < n then
//当前不是开最后一种材料,则分别开0至Max个,保留部分料坯长度给后继材料
for i := Maxdo
wnto 0do
begin
Prj[Index] := i;
RemLen := Len - i * MatLen[Index];
//料坯长度为0,其后的材料不能再开,所以设0
if RemLen = 0 then
FillChar(Prj[Index + 1], (n - Index) * SizeOf(Integer), 0);
CalcProjectSub(RemLen, Index + 1);
end
else
begin
//最后一种材料了,设置最大的开料数量即可
Prj[Index] := Max;
CalcProjectSub(0, Index + 1);
end;
end;
begin
//初始开料方案
SetLength(MatPrj, 0);
MatPrjCnt := 0;
//由于材料的数量n不定,故用递归方案计算各开料方案
CalcProjectSub(Len, 1);
end;
function CheckSuccess(Node: PNode): Boolean;
//检查当前的方案是否已经满足MatQty的要求
var
i: Integer;
begin
Result := True;
for i := 1 to ndo
if Node.MatCnt < MatQty then
begin
//只要其中一个小于MatQty,则为不满足
Result := False;
Break;
end;
end;
procedure ShowSolution(Node: PNode);
//显示第一个解决方案
var
s: String;
i: Integer;
begin
s := Format('所需要的最少料坯数量: %d'#13#10'开料方案如下:'#13#10,
[Node.Count]);
while Assigned(Node)do
begin
for i := 1 to ndo
AppendStr(s, IntToStr(MatPrj[Node.PrjIndex, i]) + ' ');
AppendStr(s, #13#10);
Node := Node.Parent;
end;
ShowMessage(s);
end;
procedure OptimalProject;
//求解最优方案
var
List: TNodeList;
Node, NewNode: PNode;
i, j, p: Integer;
begin
//建立队列
List := TNodeList.Create;
try
//初始化队列中的元素,即把各种方案入队
for i := 0 to MatPrjCnt - 1do
begin
New(Node);
with Node^do
begin
Count := 1;
Move(MatPrj[1], MatCnt[1], SizeOf(MatCnt));
PrjIndex := i;
Parent := nil;
end;
List.Add(Node);
//检查是否某一种开料方案即可满足要求
if CheckSuccess(Node) then
begin
ShowSolution(Node);
Exit;
end;
end;
p := 0;
while p < List.Countdo
begin
//从列队中取出第一个节点
Node := List[p];
//在第一个节点基础上,在分别开一条料坯并入队列
for i := Node.PrjIndex to MatPrjCnt - 1do
begin
New(NewNode);
with NewNode^do
begin
Count := Node.Count + 1;
Move(Node.MatCnt[1], MatCnt[1], SizeOf(MatCnt));
for j := 1 to ndo
Inc(MatCnt[j], MatPrj[i, j]);
PrjIndex := i;
Parent := Node;
end;
List.Add(NewNode);
//检测是否开了这一条料坯后,可以达到要求
if CheckSuccess(NewNode) then
begin
ShowSolution(NewNode);
Exit;
end;
end;
Inc(p);
end;
finally
List.Free;
end;
end;
{ TNodeList }
function TNodeList.GetItem(Index: Integer): PNode;
begin
Result := inherited Items[Index];
end;
procedure TNodeList.Notify(Ptr: Pointer;
Action: TListNotification);
begin
if Action = lnDeleted then
Dispose(PNode(Ptr));
inherited;
end;
调用方法
begin
CalcProject;
OptimalProject;
end;
如果要考虑料头或者料间,或者需要多种解决方案,适当修改即可.