【算法】任务分配问题(蛮力+回溯+分支限界) C++实现
一、简单算法最简单的算法是:蛮力法,这里采用增量穷举法求出所有的分配方案ps,再计算出每种方案的成本,比较求出最小成本的方案,即最优方案。1)实现代码#include<iostream>#include<vector>using namespace std;#define INF 9999//最大成本值#define MAXN 21int n;int c[MAXN][MAX
·
分别用蛮力法、回溯法、分支限界法求解任务分配问题。
任务分配问题描述:
假设有n个任务需要分配给n个人执行,每个人只执行一个任务,每个任务只由一个人执行。第i个人执行第j个任务的成本是Cij(1<=i,j<=n), 求解初最小成本的分配方案。
一、简单算法
最简单的算法是:蛮力法,这里采用增量穷举法求出所有的分配方案ps,再计算出每种方案的成本,比较求出最小成本的方案,即最优方案。
1)实现代码
#include<iostream>
#include<vector>
using namespace std;
#define INF 9999 //最大成本值
#define MAXN 21
int n;
int c[MAXN][MAXN];
vector<vector<int>> ps;
void Insert(vector<int> s, int i, vector<vector<int>> &ps1) {
//在每个集合元素中插入i得到ps1
vector<int> s1;
vector<int>::iterator it;
for (int j = 0; j < i; j++) //在s的每个位置都插入i
{
s1 = s;
it = s1.begin() + j; //求出插入位置
s1.insert(it, i); //插入整数i
ps1.push_back(s1); //添加到ps1中
}
}
void Perm(int n)
{
vector<vector<int>> ps1; //临时存放子排列
vector<vector<int>>::iterator it; //全排列迭代器
vector<int> s, s1;
s.push_back(1);
ps.push_back(s); //添加{1}集合元素
for (int i = 2; i <= n; i++) //循环添加1~n
{
ps1.clear(); //ps1存放插入i的结果
for (it = ps.begin(); it != ps.end(); ++it)
{
Insert(*it, i, ps1); //在每个集合元素中间插入i得到ps1
}
ps = ps1;
}
}
void Allocate(int n, int& mini, int& minc) //求任务分配问题的最优方案
{
Perm(n);
for (int i = 0; i < ps.size(); i++) //求出每个方案的成本
{
int cost = 0;
for (int j = 0; j < ps[i].size(); j++)
{
cost += c[j][ps[i][j] - 1];
}
if (cost < minc)
{
minc = cost;
mini = i;
}
}
}
int main()
{
int mincost = INF, mini;
cin >> n;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
cin >> c[i][j];
Allocate(n, mini, mincost);
printf("最优方案:\n");
for (int k = 0; k < ps[mini].size(); k++)
{
printf("第%d个人安排任务%d\n", k + 1, ps[mini][k]);
}
printf("总成本=%d\n", mincost);
return 0;
}
2)运行结果图
3)复杂度分析
蛮力法求解任务分配问题的时间复杂度为:O( n x n!)
二、回溯算法
1)实现代码
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
#define INF 9999
#define MAXN 21
int n;
int c[MAXN][MAXN];
int x[MAXN]; //临时解
int cost = 0; //临时解的成本
int bestx[MAXN]; //最优解
int mincost = INF; //最优解的成本
bool worker[MAXN]; //worker[j]表示任务j是否已经分配人员
void allocate(int i) //为第i个人员分配任务
{
if (i > n) //到达叶子节点
{
if (cost < mincost)
{
mincost = cost;
for (int j = 1; j <= n; j++)
bestx[j] = x[j];
}
}
else
{
for (int j = 1; j <= n; j++) { //为人员i试探任务j分配情况,从1到n
if (!worker[j]) //任务j还没分配
{
worker[j] = true;
x[i] = j; //任务j分配给i人员
cost += c[i][j];
allocate(i + 1); //为人员i+1分配任务
worker[j] = false; //回溯
x[i] = 0;
cost -= c[i][j];
}
}
}
}
int main() {
memset(worker, 0, sizeof(worker));
cin >> n;
for (int i = 1; i <=n; i++)
for (int j = 1; j <= n; j++)
cin >> c[i][j];
allocate(1); //从人员1开始尝试分配
printf("最优方案:\n");
for (int k = 1; k <= n; k++)
{
printf("第%d个人安排任务%d\n", k ,bestx[k]);
}
printf("总成本=%d\n", mincost);
return 0;
}
2)运行结果图
3)复杂度分析
回溯算法求解任务分配问题中,每个人员试探1~n个任务,对应的解空间树是一颗n叉树,算法时间复杂度为:O(n^n)
三、分支限界算法
这里采用队列式的分治限界法求解。
1)实现代码
#include<iostream>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f //定义无穷大
#define MAXN 21
int n;
int c[MAXN][MAXN]; //人员任务-成本矩阵
int bestx[MAXN]; //最优分配方案
int mincost = INF; //最小成本
int total = 1; //结点个数累计
struct NodeType //队列结点类型
{
int no; //结点编号
int i; //人员编号
int x[MAXN]; //x[i]为人员i分配的任务编号
bool worker[MAXN]; //worker[i]=true表示任务i已经被分配
int cost; //当前已分配任务的总成本
int lb; //下界
bool operator<(const NodeType& s) const //重载<关系函数>
{
return lb > s.lb;
}
};
void bound(NodeType& e) { //求结点e的界限值
int minsum = 0;
for (int i = e.i + 1; i <= n; i++) { //求e[e.i+1...n]行中的最小元素
int minc = INF;
for (int j = 1; j <= n; j++)
if (e.worker[j] == false && c[i][j] < minc)
minc = c[i][j];
minsum += minc;
}
e.lb = e.cost + minsum;
}
void bfs() {
int j;
NodeType e, e1;
priority_queue<NodeType>qu;
memset(e.x, 0, sizeof(e.x)); //初始化根节点的x值
memset(e.worker, 0, sizeof(e.worker)); //初始化根节点的worker
e.i = 0;
e.cost = 0;
bound(e); //求根节点的lb
e.no = total++;
qu.push(e); //根节点进队列
while (!qu.empty()) {
e = qu.top(); qu.pop(); //弹出队列结点e,当前考虑人员e.i
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; //拓展分配下一个人员的任务,对应结点e1
for (j = 1; j <= n; j++) { //考虑n个任务
if (e.worker[j]) //任务j是否已经分配人员,若已分配则跳过
continue;
for (int i = 1; i <= n; i++) {
e1.x[i] = e.x[i];
}
e1.x[e1.i] = j; //为人员e1.i分配任务j
for (int k = 1; k <= n; k++) {
e1.worker[k] = e.worker[k];
}
e1.worker[j] = true; //表示任务j已经分配
e1.cost = e.cost + c[e1.i][j];
bound(e1); //求e1的lb
e1.no = total++;
if (e1.lb <= mincost) //剪枝
qu.push(e1);
}
}
}
int main() {
cout << "输入人员数/任务数的大小:n" << endl;
cin >> n;
cout << "输入人员-任务成本值("<<n<<"x"<<n<<"矩阵):" << endl;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> c[i][j];
}
}
bfs();
printf("最优方案:\n");
for (int k = 1; k <= n; k++)
printf("第%d个人员分配第%d个任务\n",k, bestx[k]);
cout <<"总成本="<< mincost;
return 0;
}
2)运行结果图
3)复杂度分析
分支限界算法求解任务分配问题的时间复杂度为:O(n^n)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献1条内容
所有评论(0)