分枝限界法求解流水线作业调度问题

问题描述

有n个作业(编号为1~n)要在由两台机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是先在M1上加工,然后在M2上加工。M1和M2加工作业i所需的时间分别为ai和bi(1≤i≤n)。
流水作业调度问题要求确定这n个作业的最优加工顺序,使得从第一个作业在机器M1上开始加工,到最后一个作业在机器M2上加工完成所需的时间最少。可以假定任何作业一旦开始加工,就不允许被中断,直到该作业被完成,即非优先调度。

问题求解

作业编号为1到n,调度方案的执行步骤为1到n,解空间每一层对应一个步骤的作业分配。
根结点对应步骤0(虚),依次为步骤1、2、…、n分配任务,叶子结点对应步骤n。
叶子节点为一个可行解,比较求最优解
对于按1~n顺序执行的某种调度方案,f1表示在M1上执行完当前第i步的作业对应的总时间,f2数组表示在M2上执行完当前第i步的作业的总时间。
若第i步执行作业j,计算公式如下:
f1=f1+a[j];
f2[i]=max(f1,f2[i-1])+b[j]
这里由于每个结点中都保存了f1和f2,因此可以将f2数组改为单个变量。将每个队列结点的类型声明如下:

struct NodeType		//队列结点类型
{  int no;			//结点编号
   int x[MAX];			//x[i]表示第i步分配作业编号
   int y[MAX];			//y[i]=1表示编号为i的作业已经分配
   int i;			//步骤编号
   int f1;			//已经分配作业M1的执行时间
   int f2;			//已经分配作业M2的执行时间
   int lb;			//下界
   bool operator<(const NodeType &s) const	//重载<关系函数
   {
	return lb>s.lb;	//lb越小越优先出队
   }
};

对应的求结点e的lb的算法如下:

void bound(NodeType &e)	//求结点e的限界值	
{  int sum=0;
   for (int i=1;i<=n;i++)	//扫描所有作业
     if (e.y[i]==0) sum+=b[i];
				//仅累计e.x中还没有分配的作业的b时间
   e.lb=e.f2+sum;
}

用bestf(初始值为∞)存放最优调度时间。
bestx数组存放当前作业最优调度。
采用的剪枝原则是,仅仅扩展 e.lb<bestf 的结点。

代码

//问题表示
int n=4;			//作业数
int a[MAX]={0,5,12,4,8};	//M1上的执行时间,不用下标0的元素
int b[MAX]={0,6,2,14,7};	//M2上的执行时间,不用下标0的元素
//求解结果表示
int bestf=INF;			//存放最优调度时间
int bestx[MAX];		//存放当前作业最佳调度
int total=1;			//结点个数累计

struct NodeType		//队列结点类型
{  int no;			//结点编号
   int x[MAX];			//x[i]表示第i步分配作业编号
   int y[MAX];			//y[i]=1表示编号为i的作业已经分配
   int i;			//步骤编号
   int f1;			//已经分配作业M1的执行时间
   int f2;			//已经分配作业M2的执行时间
   int lb;			//下界
   bool operator<(const NodeType &s) const	//重载<关系函数
   {
	return lb>s.lb;	//lb越小越优先出队
   }
};

void bound(NodeType &e)
{
    int sum=0;
    for(int i=1;i<=n;i++)
        if(e.y[i]==0)//当前作业未被分配
            sum+=b[i];
    e.lb=e.f2+sum;
}

void bfs()
{
    NodeType e,e1;
    priority_queue<NodeType> qu;

    memset(e.x,0,sizeof(e.x));
    messet(e.y,0,sizeof(e.y));
    e.i=0;
    e.f1=0;
    e.f2=0;
    bound(e);
    e.no=total++;
    qu.push(e);

    while(!qu.empty())
    {
        e=qu.top();
        qu.pop();
        if(e.i==n)
        {
            if(e.f2<bestf)
            {
                bestf=e.f2;
                for(int j=1;j<=n;j++)
                    bestx[j]=e.x[j];
            }
        }

        e1.i=e.i+1;
        for(int j=1;j<=n;j++)
        {
            if(e.y[j]==1)
                continue;
            for(int i=1;i<=n;i++)
                e1.x[i]=e.x[i];
            e1.x[e1.i]=j;
            for(int i=1;i<=n;i++)
                e1.y[i]=e.y[i];
            e1.y[j]=1;
            e1.f1=e.f1+a[j];
            e1.f2=max(e1.f1,e.f2)+b[j];
            bound(e1);
            if(e1.lb<bestf)
            {
                e1.no=total++;
                qu.push(e1);
            }
        }
    }
}

改进

在扩展每个子结点时判断是否为叶子结点,若是则产生一个可行解,比较产生最优解,该叶子结点不进队;若不是叶子结点则将其进队。

//问题表示
int n=4;			//作业数
int a[MAX]={0,5,12,4,8};	//M1上的执行时间,不用下标0的元素
int b[MAX]={0,6,2,14,7};	//M2上的执行时间,不用下标0的元素
//求解结果表示
int bestf=INF;			//存放最优调度时间
int bestx[MAX];		//存放当前作业最佳调度
int total=1;			//结点个数累计

struct NodeType		//队列结点类型
{  int no;			//结点编号
   int x[MAX];			//x[i]表示第i步分配作业编号
   int y[MAX];			//y[i]=1表示编号为i的作业已经分配
   int i;			//步骤编号
   int f1;			//已经分配作业M1的执行时间
   int f2;			//已经分配作业M2的执行时间
   int lb;			//下界
   bool operator<(const NodeType &s) const	//重载<关系函数
   {
	return lb>s.lb;	//lb越小越优先出队
   }
};

void bound(NodeType &e)
{
    int sum=0;
    for(int i=1;i<=n;i++)
        if(e.y[i]==0)//当前作业未被分配
            sum+=b[i];
    e.lb=e.f2+sum;
}

void bfs()
{
    NodeType e,e1;
    priority_queue<NodeType> qu;

    memset(e.x,0,sizeof(e.x));
    messet(e.y,0,sizeof(e.y));
    e.i=0;
    e.f1=0;
    e.f2=0;
    bound(e);
    e.no=total++;
    qu.push(e);

    while(!qu.empty())
    {
        e=qu.top();
        qu.pop();

        e1.i=e.i+1;
        for(int j=1;j<=n;j++)
        {
            if(e.y[j]==1)
                continue;
            for(int i=1;i<=n;i++)
                e1.x[i]=e.x[i];
            e1.x[e1.i]=j;
            for(int i=1;i<=n;i++)
                e1.y[i]=e.y[i];
            e1.y[j]=1;
            e1.f1=e.f1+a[j];
            e1.f2=max(e1.f1,e.f2)+b[j];
            bound(e1);
            if(e1.i==n)//到达叶子节点
            {
                if(e1.f2<bestf)
                {
                    bestf=e1.f2;
                    for(int j=1;j<=n;j++)
                        bestx[j]=e1.x[j];
                }
            }
            else if(e1.lb<bestf)
            {
                e1.no=total++;
                qu.push(e1);
            }
        }
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值