算法篇-10-回溯法-工作分配&世界名画监视

本系列所有代码https://github.com/YIWANFENG/Algorithm-github


工作分配

n个工作分给n个人,将i工作给j号人所需费用为c[i][j],设计一算法为每人分配一工作,并且耗费最小。

算法思路分析与相关公式:

这题也是一简单的排列树问题,方式类似于旅行售货员问题,不同之处在于这里不需要判断是否有回路,所以变得更简单了。

 

程序源代码:

#include <iostream>
#include <algorithm>
//工作分配问题-回溯法
//排列树
using namespace std;
class WorkAssign{
private:
       intn;                     //n个人
       int*time;              //(n+1)*(n+1)矩阵,表示i分给j所需的费用
       int   *best_c;        //最优解
       int*curr_c;     //当前最优分配
       intcurr_t;              //当前耗费
       intbest_t;              //最优耗费
 
private:
       voidBacktrack(int t)
       {
              //第i个人
               
              if(t>n&&curr_t<best_t) {
                     for(inti=0;i<=n;++i) {
                            best_c[i]=curr_c[i];
                            best_t = curr_t;
                     }
                     return;
              }
              for(int i=t; i<=n; ++i) {
                     curr_c[t]= i;
                     curr_t+= time[t*(n+1)+i];
                     swap<int>(curr_c[t],curr_c[i]);
                     Backtrack(t+1);
                     swap<int>(curr_c[t],curr_c[i]);
                     curr_t-= time[t*(n+1)+i];
                     curr_c[t]= 0;
              }
       }
 
public:
       intSolve(int n_,int *time_,int *best,int max_time)
       {
              n = n_;
              time = time_;
              best_c = best;
              curr_c = new int[(n+1)*(n+1)];
              curr_t = 0;
              best_t = max_time;
              Backtrack(1);
             
              delete []curr_c;
              return best_t;
       }
 
 
};
int main()
{
       intn=5;
       inttime_[] = {
              0,0,0,0,0,0,
              0,1,2,3,4,5,
              0,5,1,2,3,4,
              0,4,4,1,1,4,
              0,4,2,4,1,5,
              0,5,4,2,3,1
       };
       intwu[n+1];
       WorkAssignwa;
       cout<<"最小耗费"<<wa.Solve(n,time_,wu,50)<<endl;
       cout<<"作业安排"<<endl;
       for(inti=1; i<=n; ++i) {
              cout<<wu[i]<<' ';
       }
       cin.get();
       return0;
}


 

缺点分析:

在这里同样没有加限界函数,其中一限界方法同于旅行售货员问题,若当前耗费超过以求出的最优耗费,则不必搜索当前结点的子树。

 

世界名画监视问题

题目:

世界名画陈列馆由m×n个排列成矩形阵列的陈列室组成。为了防止名画被盗,需要在陈列室中设置警卫机器人哨位。每个警卫机器人除了监视它所在的陈列室外,还可以监视与它所在的陈列室相邻的上、下、左、右4 个陈列室。试设计一个安排警卫机器人哨位的算法,使得名画陈列馆中每一个陈列室都在警卫机器人的监视之下,且所用的警卫机器人数最少。

 

算法思路分析以及相关公式:

欲监视这个m*n矩阵,我们先来假设在安排第n号房间时,从0~(n-1)号房间安排完毕,即下图黑色阴影部分安排完毕,在安排?所标位置时,可安放机器人位置为五个有五角星标注的位置,经过分析可知上、左五角星不可能获得最优安排,因为他们安排后分别只能多监视1、2个房间,所以下一步应安排在中、右、下这三位置中某一个。

那么判断回溯结束即时房间全部安全,即不安全房间为0,此时应记录当前最优解与当前全局最优解比较。回溯时我们要变动对房间的监视情况,用一整数表示某房间监视情况,有一机器人监视到他就+1,此时该房间若为1则不安全房间减一,这样在回溯时取消机器人后就应考虑-1,若该房间为0则不安全房间加一。

 

程序源代码:

#include <iostream>
//世界名画排列问题
//回溯法
//算法时间复杂度不理想啊,7*7矩阵以上复杂度陡增。
//8*8矩阵用我这电脑跑了近50分钟
using namespace std;
class Point{
public:
       intx,y;
       Point(intx_,int y_) {
              x=x_;y=y_;
       }
       Point(){
              x=0;y=0;
       }
};
 
class Monitor{
private:
       int*rooms;    // m*n矩阵表示房间被几人监视
       intm;                    //m行
       intn;                    //n列
      
       bool*robot;    //m*n矩阵表示当前解的机器人安排
       bool*result;   //返回安排结果
      
       intbestc;        //最少机器哨位数
       intcurr_c;             //当前机器哨位数
      
       intnot_safe;    //当前不安全房间数
      
      
private:
       voidChange(Point &t)
       {
              //room,not_safe与 robot变动
              //中间
              if(t.x>0&&t.y>0&&t.x<=m&&t.y<=n){
                     robot[t.x*(n+1)+t.y]= true;
                     rooms[t.x*(n+1)+t.y]++;
                     if(rooms[t.x*(n+1)+t.y]==1)not_safe--;
              } else {
                     return;
              }
             
             
              //上
              if(t.x-1>0&&t.y>0&&t.x-1<=m&&t.y<=n){
                     rooms[(t.x-1)*(n+1)+t.y]++;
                     if(rooms[(t.x-1)*(n+1)+t.y]==1)not_safe--;
              }
              //右
              if(t.x>0&&t.y+1>0&&t.x<=m&&t.y+1<=n){
                     rooms[t.x*(n+1)+t.y+1]++;
                     if(rooms[t.x*(n+1)+t.y+1]==1)not_safe--;
              }
              //下
              if(t.x+1>0&&t.y>0&&t.x+1<=m&&t.y<=n){
                     rooms[(t.x+1)*(n+1)+t.y]++;
                     if(rooms[(t.x+1)*(n+1)+t.y]==1)not_safe--;
              }
              //左
              if(t.x>0&&t.y-1>0&&t.x<=m&&t.y-1<=n){
                     rooms[t.x*(n+1)+t.y-1]++;
                     if(rooms[t.x*(n+1)+t.y-1]==1)not_safe--;
              }
       }
      
       voidRecover(Point &t)
       {
              //room,not_safe与 robot恢复
              if(t.x>0&&t.y>0&&t.x<=m&&t.y<=n){
                     robot[t.x*(n+1)+t.y]= false;
                     rooms[t.x*(n+1)+t.y]--;
                     if(rooms[t.x*(n+1)+t.y]==0)not_safe++;
              } else {
                     return;
              }
             
              //上
              if(t.x-1>0&&t.y>0&&t.x-1<=m&&t.y<=n){
                     rooms[(t.x-1)*(n+1)+t.y]--;
                     if(rooms[(t.x-1)*(n+1)+t.y]==0)not_safe++;
              }           
              //右
              if(t.x>0&&t.y+1>0&&t.x<=m&&t.y+1<=n){
                     rooms[t.x*(n+1)+t.y+1]--;
                     if(rooms[t.x*(n+1)+t.y+1]==0)not_safe++;
              }
              //下
              if(t.x+1>0&&t.y>0&&t.x+1<=m&&t.y<=n){
                     rooms[(t.x+1)*(n+1)+t.y]--;
                     if(rooms[(t.x+1)*(n+1)+t.y]==0)not_safe++;
              }                  
              //左 
              if(t.x>0&&t.y-1>0&&t.x<=m&&t.y-1<=n){
                     rooms[t.x*(n+1)+t.y-1]--;
                     if(rooms[t.x*(n+1)+t.y-1]==0)not_safe++;
              }           
       }
      
       PointFindNotSafe(Point &t)const
       {
              //找到一个不安全房屋
              for(int i=t.x; i<=m; ++i) {
                     for(intj=1; j<=n; ++j) {
                            if(i>0&&rooms[i*(n+1)+j]==0) {
                                   //cout<<"("<<i<<","<<j<<")"<<endl;
                                   returnPoint(i,j);
                            }
                     }
              }
              return Point(0,0);//超界坐标
       }
      
       voidBacktrack(Point t)
       {
              //if(not_safe<0) {
              //     cout<<"("<<t.x<<","<<t.y<<")"<<endl;
              //     cout<<"Notsafe"<<not_safe<<" C "<<curr_c<<endl;
              //     return;
              //}
             
              if(not_safe==0&&curr_c<bestc){
                     for(inti=0; i<(m+1)*(n+1); ++i) {
                            result[i] = robot[i];
                           
                            //cout <<"#"<<result[i]<<"#"<< endl;
                     }
                     bestc=curr_c;
                     return;
              }
              //超界检查
              if(t.x<=0 || t.y<=0 || t.x>m ||t.y>n)
                     return;
              Point x1,x2;
              //(t.x,t.y)
              Change(t);
              curr_c++;
              x1 = FindNotSafe(t);
              //cout << "(" <<x1.x << "," << x1.y << ")" << endl;
              Backtrack(x1);
              Recover(t);
              curr_c--;
             
              //(t.x,t.y+1)
              x2.x = t.x;
              x2.y = t.y+1;
              if (x2.x > 0 && x2.y > 0&& x2.x <= m && x2.y <= n) {
                     Change(x2);
                     curr_c++;
                     x1= FindNotSafe(t);
                     Backtrack(x1);
                     Recover(x2);
                     curr_c--;
              }
             
             
              //(t.x+1,t.y)
              x2.x = t.x+1;
              x2.y = t.y;
              if (x2.x > 0 && x2.y > 0&& x2.x <= m && x2.y <= n) {
                     Change(x2);
                     curr_c++;
                     x1= FindNotSafe(t);
                     Backtrack(x1);
                     Recover(x2);
                     curr_c--;
              }           
       }
      
      
public:
       intSolve(int m_,int n_,bool *res)
       {
              //m_ 行 n_列 res[] 最终安排数组
              //返回所需最少机器人属
              m =m_; n=n_;
              rooms=(int*)malloc(sizeof(int)*(n+1)*(m+1));
              robot=(bool*)malloc(sizeof(bool)*(n+1)*(m+1));
              for(int i=0;i<(n+1)*(m+1);++i) {
                     rooms[i]= 0;
                     robot[i]= false;
              }
              result = res;
              bestc = m*n;
              curr_c = 0;
              not_safe=m*n;       //当前不安全房间数
              Backtrack(Point(1,1));
              free(rooms);
              free(robot);
              return bestc;
       }
             
};
 
void show(int m_,int n_,bool *res)
{
       for(inti=1; i<=m_; ++i) {
              for(int j=1; j<=n_; ++j) {
                     if(res[i*(n_+1)+j])cout<<"# ";
                     elsecout<<"O ";
              }
              cout<<'\n';
       }
}
 
 
 
int main(int argc,char *argv[])
{
       intn = 4, m = 4;
       cout<< "输入房间矩阵大小行列";
       cin>> m >> n;
       Monitorsl;
       bool*res = new bool[(n+1)*(m+1)];
       cout<<"最少哨兵:"<<sl.Solve(m,n,res)<<endl;
       show(m,n,res);
      
       intaa;
       cin>>aa;
      
       return0;
}

运行结果分析:

算法时间复杂度不理想啊,7*7矩阵以上复杂度陡增。

8*8矩阵用我这电脑跑了近50分钟 。优化方向应是增加一些剪枝函数加以限制。

提到过结点控制,但是我此时没有想出来。


  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值