回溯法-工作分配

ACM中的工作分配问题是一个典型的回溯问题,利用回溯思想能很准确地得到问题的解。下面就这个问题好好分析下。 


问题描述: 
    设有n件工作分配给n个人。为第i个人分配工作j所需的费用为c[i][j] 。试设计一个算法,计算最佳工作分配方案,为每一个人都分配1 件不同的工作,并使总费用达到最小。 


解题思路: 
    由于每个人都必须分配到工作,在这里可以建一个二维数组c[i][j],用以表示i号工人完成j号工作所需的费用。给定一个循环,从第1个工人开始循环分配工作,直到所有工人都分配到。为第i个工人分配工作时,再循环检查每个工作是否已被分配,没有则分配给i号工人,否则检查下一个工作。可以用一个一维数组x[j]来表示第j号工作是否被分配,未分配则x[j]=0,否则x[j]=1。利用回溯思想,在工人循环结束后回到上一工人,取消此次分配的工作,而去分配下一工作直到可以分配为止。这样,一直回溯到第1个工人后,就能得到所有的可行解。在检查工作分配时,其实就是判断取得可行解时的二维数组的一下标都不相同,二下标同样不相同。 


样例分析: 
    给定3件工作,i号工人完成j号工作的费用如下: 
10 2 3 
2 3 4 
3 4 5 


    假定一个变量count表示工作费用总和,初始为0,变量i表示第i号工人,初始为1。n表示总的工作量,这里是取3。c[i][j]表示i号工人完成j号工作的费用,x[j]表示j号工作是否被分配。算法如下: 

void work ( int i , int count ){     if ( i > n )       return ;     for ( int j = 1 ; j <= n ; j ++)       if ( x [ j ] == 0 ){         x [ j ] = 1 ;         work ( i + 1 , count + c [ i ][ j ]);         x [ j ] = 0 ;       }   }  

 那么在这里,用回溯法的思想就是,首先分配的工作是: 
10:c[1][1]  3:c[2][2]  5:c[3][3]  count=18; 
    
    此时,所有工人分配结束,然后回溯到第2个工人重新分配: 
10:c[1][1]  4:c[2][3]  4:c[3][2]  count=18; 


    第2个工人已经回溯到n,再回溯到第1个工人重新分配: 
2:c[1][2]  2:c[2][1]  5:c[3][3]  count=9; 


    回溯到第2个工人,重新分配: 
2:c[1][2]  4:c[2][3]  3:c[3][1]  count=9; 


    再次回溯到第1个工人,重新分配: 
3:c[1][3]  2:c[2][1]  4:c[3][2]  count=9; 


    回溯到第2个工人,重新分配: 
3:c[1][3]  3:c[2][2]  3:c[3][1]  count=9; 


    这样,就得到了所有的可行解。而我们是要得到最少的费用,即可行解中和最小的一个,故需要再定义一个全局变量cost表示最终的总费用,初始cost为c[i][i]之和,即对角线费用相加。在所有工人分配完工作时,比较count与cost的大小,如果count小于cost,证明在回溯时找到了一个最优解,此时就把count赋给cost。 
    到这里,整个算法差不多也快结束了,已经能得到最终结果了。但考虑到算法的复杂度,这里还有一个剪枝优化的工作可以做。就是在每次计算局部费用变量count的值时,如果判断count已经大于cost,就没必要再往下分配了,因为这时得到的解必然不是最优解。 


测试代码:

#include<iostream> using namespace std; int n,cost=0; int x[100],c[100][100]; void work(int i,int count){ if(i>n && count<cost){ cost = count; return ; } if(count<cost) for(int j=1;j<=n;j++) if(x[j] == 0){ x[j] = 1; work(i+1,count+c[i][j]); x[j] = 0; } } int main(){ cin>>n; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ cin>>c[i][j]; x[j] = 0; } cost+=c[i][i]; } work(1,0); cout<<cost<<endl; system("pause"); return 0; }

//工作分配问题 /* 3 10 2 3 2 3 4 3 4 5 */ #include <iostream> #include <algorithm> using namespace std; int table[21][21],n,best,r[21]; int compute(int k) {     int temp=0,i;     for(i=1;i<=k;i++)         temp+=table[i][r[i]];     return temp; } void search(int k) {     if(k==n)     {         int temp=compute(n);         if(temp<best)             best=temp;         return;     }     for(int i=k;i<=n;i++)     {         swap(r[i],r[k]);         if(compute(k) < best)             search(k+1);         swap(r[i],r[k]);     } } int main() {     int i,j;     while(cin>>n,n)     {         for(i=1;i<=n;i++)             for(j=1;j<=n;j++)                 cin>>table[i][j];         for(i=1;i<=n;i++)             r[i]=i;         best=INT_MAX;         search(1);         cout<<best<<endl;     }     return 0; }

运行过程说明: jobAssignBacktrack.exe 输入源数据文件路径+文件名 在同目录下可以查看输出文件output.txt ★问题描述 有n份作业分配给n个人去完成,每人完成一份作业。假定第i个人完成第j份作业需要花费cij时间,cij>0,1≦i,j≦n。试设计一个回溯算法,将n份作业分配给n个人完成,使得总花费时间最短。 ★格式输入 由文件input.txt给出输入数据。第一行有1 个正整数n (1≤n≤100)。 接下来的n行,每行n个数,表示工作费用。 ★格式输出 将计算出的结果输出到文件output.txt。 ★示例输入输出 5 50 87 62 56 92  43 22 98 57 36  1 5 97 96 43  58 62 27 73 27  60 71 38 71 95

144 工人: 1 执行任务: 4 工人: 2 执行任务: 2 工人: 3 执行任务: 1 工人: 4 执行任务: 5 工人: 5 执行任务: 3 ★算法设计(包括算法设计过程中相关内容的说明、数据结构的选择、 算法详细描述及算法分析): ●1.设计说明: 算法中使用job[i]记录作业i是否已被分配,若被分配则置1,不必再搜索其分支;、 assign(int k,unsigned int cost) cost用来记录当前作业分配的开销,作为参数传递给下一层递归调用。 ●2.算法设计: ●2.1数据结构: #define N 100 //N表示最大任务数和工人数 int c[N][N]; //c[i][j] 表示工人i执行作业j所用的时间 unsigned int mincost = 65535; //设置的初始值,大于可能的费用 int task[N], //当前作业分配情况: 工人i执行task[i]  bestSolution[N], //最优作业分配情况: 工人i执行作业bestSolution[i] job[N]; //记录作业是否已被分配给工人,1表示已分配,0为分配 int njob; //实际工作、工人数 主要操作: void input(int& njob, int c[][N]); //读入工人、作业数njob,开销矩阵c[][N] //初始化task,bestSolution,job,读入工人、作业数njob,开销矩阵c[][N] void initData(int* task, int* bestSolution, int* job, int& njob, int c[][N]); void assign(int k,unsigned int cost); //为工人k分配作业,cost:当前作业分配后的花费 //输出结果:mincost、作业分配情况 void outputResult(int minCost,int njob, int* bestSolution); ●2.2算法描述: void assign(int k,unsigned int cost) //为工人k分配作业,cost:当前作业分配后的花费 { int i; if(k > njob && cost < mincost) //搜索至叶节点,判断是否更优 { mincost = cost; for(i=1; i<=njob; i++) bestSolution[i] = task[i]; } else { for(i=1;i<=njob;i++)  {  if(job[i]==0 && cost+c[k][i])//约束函数,job[i]==0则为被分配 { job[i] = 1; task[k] = i; assign(k+1,cost+c[k][i]); job[i] = 0; task[k] = 0; } } } } ●2.3算法分析 本算法的最坏时间复杂度为:O(n!) 最坏空间复杂度为:O(n)

 

[c-sharp]  view plain copy print ?
  1. //=======================================================================  
  2. //jobAssign.cpp  
  3. //将n份作业分配给n个人完成,使得总花费时间最短,回溯法  
  4. //by leo  
  5. //5.13.2011  
  6. //=======================================================================  
  7. #include <iostream>  
  8. #include<fstream>  
  9. #include<string>  
  10. using namespace std;  
  11. //-----------------------------------------------------------------------  
  12. #define N 100                 //N表示最大任务数和工人数  
  13. int c[N][N];                  //c[i][j] 表示工人i执行作业j所用的时间  
  14. unsigned int mincost = 65535; //设置的初始值,大于可能的费用  
  15. int task[N],                  //当前作业分配情况:工人i 执行task[i]  
  16.     bestSolution[N],          //最优作业分配情况: 工人i执行作业bestSolution[i]  
  17.     job[N];                   //记录作业是否已被分配给工人,1表示已分配,0为分配  
  18. int njob;                     //实际工作、工人数  
  19. //-----------------------------------------------------------------------  
  20. void input(int& njob, int c[][N]);    //读入工人、作业数njob,开销矩阵c[][N]  
  21.                                       //初始化task,bestSolution,job,读入工人、作业数njob,开销矩阵c[][N]  
  22. void initData(int* task, int* bestSolution, int* job, int& njob, int c[][N]);  
  23. void assign(int k,unsigned int cost); //为工人k分配作业,cost:当前作业分配后的花费  
  24.                                       //输出结果:mincost、作业分配情况  
  25. void outputResult(int minCost,int njob, int* bestSolution);  
  26. //-----------------------------------------------------------------------  
  27. void main()  
  28. {  
  29.     initData(task, bestSolution, job, njob, c);  
  30.     assign(1,0); //从任务1开始分配  
  31.     outputResult(mincost, njob,bestSolution);  
  32.     system("pause");  
  33. }  
  34. //-----------------------------------------------------------------------  
  35. //读入工人、作业数njob,开销矩阵c[][N]  
  36. //-----------------------------------------------------------------------  
  37. void input(int& njob, int c[][N])  
  38. {  
  39.     string str;  
  40.     cout << "Please input the input file name"  
  41.          << "(input_assign04_00.txt~input_assign04_05.txt):/n";  
  42.     cin >> str;  
  43.     ifstream fin(str.c_str());  
  44.     if(!fin)  
  45.     {  
  46.         cout << "no such file,please check the file name!" <<endl;  
  47.         exit(0);  
  48.     }     
  49.     fin>>njob;      
  50.     for(int i = 1; i <= njob; i++)  
  51.         for (int j = 1; j <= njob; j++)  
  52.             fin >> c[i][j];  
  53. }  
  54. //-----------------------------------------------------------------------  
  55. //初始化task,bestSolution,job,读入工人、作业数njob,开销矩阵c[][N]  
  56. //-----------------------------------------------------------------------  
  57. void initData(int* task, int* bestSolution, int* job, int& njob, int c[][N])  
  58. {  
  59.     input(njob,c);  
  60.     for(int k =0; k <= njob; k++)  
  61.     {  
  62.         job[k] = 0;  
  63.         task[k]   = 0;   
  64.         bestSolution[k]   = 0;   
  65.     }     
  66. }  
  67. //-----------------------------------------------------------------------  
  68. //为工人k分配作业,cost:当前作业分配后的花费  
  69. //-----------------------------------------------------------------------  
  70. void assign(int k,unsigned int cost)  
  71. {  
  72.     int i;  
  73.     if(k > njob && cost < mincost)  
  74.     {  
  75.         mincost = cost;  
  76.         for(i=1; i<=njob; i++)  
  77.             bestSolution[i] = task[i];  
  78.     }  
  79.     else  
  80.     {  
  81.         for(i=1;i<=njob;i++)      
  82.         {                     
  83.             if(job[i]==0 && cost+c[k][i])  
  84.             {  
  85.                 job[i] = 1; task[k] = i;  
  86.                 assign(k+1,cost+c[k][i]);  
  87.                 job[i] = 0; task[k] = 0;  
  88.             }  
  89.         }  
  90.     }  
  91. }  
  92. //-----------------------------------------------------------------------  
  93. //输出结果:mincost、作业分配情况  
  94. //-----------------------------------------------------------------------  
  95. void outputResult(int minCost,int njob, int* bestSolution)  
  96. {  
  97.     cout<<"最小总费用:"<<mincost<<endl;  
  98.     for(int i=1; i<= njob;i++)  
  99.         cout<< "工人: "<< i <<" 执行任务: "<< bestSolution[i]  <<endl;   
  100.   
  101.     ofstream fout("output.txt");  
  102.     fout<<mincost<<endl;  
  103.     for(int j=1; j<= njob;j++)  
  104.         fout<< "工人: "<< j <<" 执行任务: "<< bestSolution[j]  <<endl;   
  105.   
  106. }  
  107. //=======================================================================  
  108.    

 

 

以下的代码稍作改变,用于c[i][j]表示作业i由工人j完成的情况

[c-sharp]  view plain copy print ?
  1. //=======================================================================  
  2. //jobAssign.cpp  
  3. //将n份作业分配给n个人完成,使得总花费时间最短,回溯法  
  4. //by leo  
  5. //5.13.2011  
  6. //=======================================================================  
  7. #include <iostream>  
  8. #include<fstream>  
  9. #include<string>  
  10. using namespace std;  
  11. //-----------------------------------------------------------------------  
  12. #define N 100                 //N表示最大任务数和工人数  
  13. int c[N][N];                  //c[i][j] 表示作业i由人j执行所做的时间  
  14. unsigned int mincost = 65535; //设置的初始值,大于可能的费用  
  15. int task[N],                  //当前作业分配情况:i由工人task[i]执行  
  16.     bestSolution[N],          //最优作业分配情况: i由工人bestSolution[i]执行  
  17.     worker[N];                //记录工人是否已被分配作业  
  18. int njob;                     //实际工作、工人数  
  19. //-----------------------------------------------------------------------  
  20. //读入工人、作业数njob,开销矩阵c[][N]  
  21. //-----------------------------------------------------------------------  
  22. void input(int& njob, int c[][N])  
  23. {  
  24.     string str;  
  25.     cout << "Please input the input file name"  
  26.          << "(input_assign02_00.txt~input_assign02_01.txt):/n";  
  27.     cin >> str;  
  28.     ifstream fin(str.c_str());  
  29.     if(!fin)  
  30.     {  
  31.         cout << "no such file,please check the file name!" <<endl;  
  32.         exit(0);  
  33.     }     
  34.     fin>>njob;      
  35.     for(int i = 1; i <= njob; i++)  
  36.         for (int j = 1; j <= njob; j++)  
  37.             fin >> c[i][j];  
  38. }  
  39. //-----------------------------------------------------------------------  
  40. //读入工人、作业数njob,开销矩阵c[][N]  
  41. //-----------------------------------------------------------------------  
  42. void initData(int* task, int* bestSolution, int* worker, int& njob, int c[][N])  
  43. {  
  44.     input(njob,c);  
  45.     for(int k =0; k <= njob; k++)  
  46.     {  
  47.         //设置每个人任务由不同工人承担时的费用及全局数组的初值  
  48.         worker[k] = 0;  
  49.         task[k]   = 0;   
  50.         bestSolution[k]   = 0;   
  51.     }     
  52. }  
  53. //-----------------------------------------------------------------------  
  54. //测试:输出c[][N]  
  55. //-----------------------------------------------------------------------  
  56. void outputInitData(int c[][N])  
  57. {  
  58.     for(int i = 1; i <= njob; i++)  
  59.     {  
  60.         for (int j = 1; j <= njob; j++)  
  61.             cout << c[i][j] <<" ";  
  62.         cout<<endl;  
  63.     }  
  64. }  
  65. //-----------------------------------------------------------------------  
  66. //k: 当前要分配的任务,cost:当前作业分配后的花费  
  67. //-----------------------------------------------------------------------  
  68. void assign(int k,unsigned int cost)  
  69. {  
  70.     int i;  
  71.     if(k > njob && cost < mincost)  
  72.     {  
  73.         mincost = cost;  
  74.         for(i=1; i<=njob; i++)  
  75.             bestSolution[i] = task[i];  
  76.     }  
  77.     else  
  78.     {  
  79.         for(i=1;i<=njob;i++)    //分配任务k  
  80.         {                     
  81.             if(worker[i]==0 && cost+c[k][i])  
  82.             {  
  83.                 worker[i] = 1; task[k] = i;  
  84.                 assign(k+1,cost+c[k][i]);  
  85.                 worker[i] = 0; task[k] = 0;  
  86.             }  
  87.         }  
  88.     }  
  89. }  
  90. //-----------------------------------------------------------------------  
  91. //输出结果:mincost、任务分配情况  
  92. //-----------------------------------------------------------------------  
  93. void outputResult(int minCost,int njob, int* bestSolution)  
  94. {  
  95.     cout<<"最小总费用:"<<mincost<<endl;  
  96.     for(int i=1; i<= njob;i++)  
  97.         cout<< "任务:"<< i <<" 由工人:"<< bestSolution[i] << "执行 " <<endl;   
  98. }  
  99. //-----------------------------------------------------------------------  
  100. void main()  
  101. {  
  102.     initData(task, bestSolution, worker, njob, c);  
  103.     assign(1,0); //从任务1开始分配  
  104.     outputResult(mincost, njob,bestSolution);  
  105.     for(int i = 1; i <= njob; i++)  
  106.         cout << bestSolution[i] << " ";  
  107.     cout <<endl;  
  108. }  
  109. //=======================================================================  
  110.    

    原文作者:回溯法
    原文地址: https://blog.csdn.net/bojue__123/article/details/41049479
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞