问题描述
假设有n个任务需要分配给n个人执行,每个人只执行一个任务,每个任务只由一个人执行。第i个人执行第j个任务的成本是Cij(1<=i,j<=n), 求解初最小成本的分配方案。
下面这个表格就是代表成本
人员 | 任务1 | 任务2 | 任务3 | 任务4 |
1 | 9 | 2 | 7 | 8 |
2 | 6 | 4 | 3 | 7 |
3 | 5 | 8 | 1 | 8 |
4 | 7 | 6 | 9 | 4 |
优先队列的分支限界法:就是谁的lb小谁先出队列,就先进行拓展,然后一下子到叶子节点得出mincost=13,然后又轮到第二层的第一个,慢慢进行比较剪枝。
struct Node //队列结点类型
{
int no; //结点编号
int i; //表示当前结点属于解空间的第i层(根节点的层次为0),即准备为人员i分配任务
int x[MAXN]; //x[i]为人员i分配的任务编号
bool worker[MAXN]; //worker[i]=true表示任务i已经分配
int cost; //已经分配任务所需要的成本
int lb; //下界
bool operator<(const Node& s) const //重载<关系函数>
{
return lb > s.lb; //这个就是lb按照升序进行排序
}
};
void bound(Node& e) //求结点e的限界值
{
int minsum = 0;
for (int i1 = e.i + 1;i1 <= n;i1++) //寻找每一行的最小值
{
int minc = INF;
for (int j1 = 1;j1 <= n;j1++)
if (e.worker[j1] == false && c[i1][j1] < minc)
minc = c[i1][j1];
minsum += minc;
}
e.lb = e.cost + minsum; //e.cost代表选择的结点成本+剩下的最小的成本
}
关于这道题,我把思路主要都写在了代码注释上,主要也是为了方便我自己理解
代码:
#include<iostream>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 21
struct Node //队列结点类型
{
int no; //结点编号
int i; //表示当前结点属于解空间的第i层(根节点的层次为0),即准备为人员i分配任务
int x[MAXN]; //x[i]为人员i分配的任务编号
bool worker[MAXN]; //worker[i]=true表示任务i已经分配
int cost; //已经分配任务所需要的成本
int lb; //下界
bool operator<(const Node& s) const //重载<关系函数>
{
return lb > s.lb;
}
};
//问题表示
int n = 4;
int c[MAXN][MAXN] = { {0},{0,9,2,7,8},{0,6,4,3,7},
{0,5,8,1,8},{0,7,6,9,4} };
//下标0的元素不用
int bestx[MAXN]; //最优分配方案
int mincost = INF; //最小成本
int total = 1; //结点个数累计
void bound(Node& e) //求结点e的限界值
{
int minsum = 0;
for (int i1 = e.i + 1;i1 <= n;i1++) //寻找每一行的最小值
{ //如果找到e这个节点,如果说他选择了某个任务的话,就不能选择那一列的值了
int minc = INF;
for (int j1 = 1;j1 <= n;j1++)
if (e.worker[j1] == false && c[i1][j1] < minc)
minc = c[i1][j1];
minsum += minc;
}
e.lb = e.cost + minsum; //e.cost代表选择的结点成本+剩下的最小的成本
//cout << e.lb << " "; //可以自行选择输出不输出
}
void bfs()
{
int j;
Node e, e1; //e,e1相当于两个儿,帮忙运进队列的
priority_queue<Node> qu;
memset(e.x, 0, sizeof(e.x)); //解向量
memset(e.worker, 0, sizeof(e.worker)); //任务是否分配
e.i = 0; //根节点也是虚结点
e.cost = 0;
bound(e);
e.no = total++;
qu.push(e);
while (!qu.empty())
{
e = qu.top();
qu.pop();
if (e.i == n)
{
if (e.cost < mincost)
{
mincost = e.cost;
for (j = 1;j <= n;j++)
bestx[j] = e.x[j];
}
}
e1.i = e.i + 1; //相当于在根节点的情况下开始拓展进行下一个节点
for (j = 1;j <= n;j++)
{
if (e.worker[j]) //查看任务j是否分配
continue;
for(int i1 = 1;i1 <= n;i1++)
e1.x[i1] = e.x[i1]; //相当于对e1初始化(1)
e1.x[e1.i] = j;
for (int i2 = 1;i2 <= n;i2++)
e1.worker[i2] = e.worker[i2]; //相当于对e1初始化(2) :::(1)(2)就相当于创建了一个新的结点并且对他初始化
e1.worker[j] = true; //这个代表的是它的第几个任务被选择
e1.cost = e.cost + c[e1.i][j];
bound(e1);
e1.no = total++;
if (e1.lb <= mincost) //剪枝
qu.push(e1);
}
}
}
int main()
{
bfs();
cout << "最优方案:" << endl;
for (int k = 1;k <= n;k++)
{
cout << "第" << k << "个人员分配第" << bestx[k] << "个任务" << endl;
}
cout << "总成本" << mincost;
return 0;
}