Description
有n件工作分配给n个人,将工作i分配给第j个人需要支付劳务费用Cij。请为每人分配一个工作,并使得总劳务费用达到最小。
输入格式
第一行一个正整数n(1<=n<=11),表示n个工作数,接下来n行,每行代表第i个工作支付给n个不同的人的劳务费用。
输出格式
两行。 第一行为最小的总劳务费用。 第二行有n个数,表示工作分配方案。 如下面sample用例的测试数据: 第二行是2 1 3,表示第1个工作分配给第2个人,第2个工作分配给第1个人,第3个工作分配给第3个人。 2+2+5=9 当同时有多种分配方案都能使得总劳务费用相同且都最小,小编号工作优先分配给小编号的人,仅输出这一种方案即可。
输入样例
3 10 2 3 2 3 4 3 4 5
输出样例
9 2 1 3
提示
n个人,任何一种站队排列,都构成搜索的情况。因此,搜索的解空间树是“排列树”。 搜索的算法可以参考书本上“批处理作业调度问题”或“旅行售货员问题”的解法,因为都是排列树的搜索。 套用排列树的回溯算法搜索框架来实现。
//
// Created by 15813 on 2019/11/20.
//
#include <iostream>
using namespace std;
//人数和工作数
int n;
//最小总工资
int minSalary = 2147483647;
//当前总工资
int curSalary;
//工资矩阵
int **salaryMatrix;
//岗位排列好,挑员工 即x[i]为员工工资
int* x;
int* opt;
void compute();
void trackback(int t);
void swap(int *x, int i, int j);
//控制输出顺序(大岗位给大号人)
int control = 0;
void swap(int *x, int i, int j){
int temp = x[i];
x[i] = x[j];
x[j] = temp;
}
int main(){
cin >> n;
salaryMatrix = new int* [n];
x = new int[n];
opt = new int[n];
for(int i = 0; i < n; ++i){
salaryMatrix[i] = new int[n];
for(int j = 0; j < n; ++j){
cin >> salaryMatrix[i][j];
}
}
for(int i = 0;i < n;i++){
x[i] = i;
}
trackback(0);
cout << minSalary << endl;
for(int i = 0; i < n; ++i){
cout << opt[i] + 1 << " " ;
}
return 0;
}
void trackback(int t){
// for(int i = 0; i < n; ++i){
// cout << opt[i] << " ";
// }
//cout << endl;
if(t >= n){
compute();
}
for(int i = t; i < n; ++i){
swap(x, i, t);
trackback(t+1);
swap(x, i, t);
}
}
void compute(){
curSalary = 0;
int curControl = 0;
//每一个岗位的工资之和
for(int i = 0; i < n; ++i){
curSalary += salaryMatrix[i][x[i]];
}
//维护最好的
if(curSalary < minSalary){
minSalary = curSalary;
for(int i = 0; i < n ; ++i){
opt[i] = x[i];
}
}
if(curSalary == minSalary){
control = 0;
for(int i = 0; i < n; ++i) {
control += (i + 1) * opt[i];
}
for(int i = 0; i < n; ++i) {
curControl += (i + 1) * x[i];
}
if(curControl > control){
control = curControl;
for(int i = 0; i < n ; ++i){
opt[i] = x[i];
}
}
}
}
回溯算法,相当于暴力全排列后求最优解,小编号工作优先分配给小编号的人一时没想到什么好方法,就用了比较笨的算i * x[i],大的说明更符合要求。