[回溯法] 和尚挑水问题-华为笔试

某寺庙里7个和尚:轮流挑水,为了和其他任务不能冲突,各人将有空天数列出如下表: 和尚1: 星期二,四; 和尚2: 星期一,六; 和尚3: 星期三,日; 和尚4: 星期五; 和尚5: 星期一,四,六; 和尚6: 星期二,五; 和尚7: 星期三,六,日; 请将所有合理的挑水时间安排表 。
输入为每个和尚从周一到周日的空闲情况,输出则为所有可能的任务安排表。
这道题用到了回溯法(backtracking),它的基本思想如下:
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
在本题中,每一种挑水策略为一个解,可从星期一开始,先选择一个空闲的和尚(如和尚1),接着到星期二再选一个和尚(空闲且非和尚1),接着依次类推直到星期日选完和尚,如果中途某天没有满足要求的和尚,则放弃该解。在每天选和尚时,都有(该天空闲和尚数-已挑过水的和尚数)种选择,通过穷举的方法,可以不断将解的范围扩大,直到遍历所有解。
下面给出具体代码:
#include <iostream>
using namespace std;
struct st
{
    int isFree[7];   //和尚某天是否空闲(1为空闲)
    bool haveWorked; //在一次解法中是否挑过水(1为挑过水)
}monk[7];

int sum;       //方案总数
int x[7]={0};  //具体方案数组,1表示挑水,0表示不挑水

void backtrack(int n)
{
    int i=0;
    if(n==7)   //得到一组解并输出
    {
        sum++;
        cout<<"第"<<sum<<"种方案:"<<endl;
        for(i=0;i<7;i++)
            cout<<x[i]<<' ';
        cout<<endl;
    }
    else
    {
        for(i=1;i<=7;++i)
        {
            if(monk[i-1].isFree[n]==1&&monk[i-1].haveWorked==false) //和尚i没有挑过水且在星期n有时间
            {
                x[n]=i;
                monk[i-1].haveWorked=true;
                backtrack(n+1);
                monk[i-1].haveWorked=false;
            }
        }
    }
}
int main()
{
    int b[7][7]={{1,0,0,1,0,1,0},{0,0,1,1,0,0,0},{0,1,0,0,1,0,1},{1,1,0,0,0,1,0},\
             {0,0,1,0,1,0,0},{0,0,0,1,0,0,1},{1,0,0,0,1,0,0}};
    int i,j;
    for(i=0;i<7;++i)    //状态初始化
    {
        for(j=0;j<7;++j)
            monk[i].isFree[j]=b[i][j];
        monk[i].haveWorked=false;
    }
    backtrack(0);
    return 0;
}
 
    原文作者:回溯法
    原文地址: https://blog.csdn.net/chenpkai/article/details/52825561
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞