回溯——四人分酒问题 收藏

来源:互联网 发布:诛仙3淘宝积分 编辑:程序博客网 时间:2024/06/02 11:20


http://blog.chinaunix.net/u/30202/showart_391485.html

http://bjydfq.itpub.net/post/7617/42768

题目:两瓶 8 两的酒,一个 3 两的杯子,瓶和杯子都没有刻度,如何将酒平均分给四个人喝?也就是每个人分 4 两酒,也没什么限制条件,当然,喝过的酒不能再吐出来(: > )。

思路:回溯算法,递归

对当前酒瓶,杯和人当前存的酒量设定为一种状态,最初始的状态为( 880 , 0000 ),最终的状态为( 000 , 4444 ),状态的改变为分酒的选择,一共有 18 种选择:

A 类:瓶子到杯子, 2 种

B 类:杯子到瓶子, 2 种

C 类:杯子到人, 4 种

D 类:瓶子到人, 8 种

E 类:瓶子到瓶子, 2 种(我没有考虑)。

F 类:人到杯子,人到瓶子,人到人不被允许。

不能用简单穷举,因为,如果穷举,要达到可行解,需要 18^24 次方这么多尝试,计算机没能力计算这么多步的。

回溯方法是深度搜索,选定一个方法一路向下走,直到没得路可走了,也就是所有的选择都到达了非法的状态,那么回到上一个状态,选择下一个选择,这样一路到达可行解。虽然不是最优的,一定是最快的。

实现代码如下:

view plaincopy to clipboardprint?
int position[6354][8];//状态集  
long ptop=0;  
//后来实验的结果是,在求解的过程中可能出现6354种状态  
int object[7]={8,8,0,0,0,0,0};//0,1瓶,2杯,3~6人,写在一起只是为了便于搜索  
int solution[60][3];//保存解过程:a,b,n a->b(n)  
int cmin=100;//最少步数  
int scount=0;  
int pour(int a,int b)  
//从a编号对象往b编号对象转移时,反回可行转移的酒量,返回0表示非法  
{  
    int empty;  
    if(object[a]==0)return 0;  
    if(a==b)return 0;  
    //if(a>2)return 0;//不能从人往容器转移  
    if(b<3)//容器往容器  
    {  
        empty=b<2?8-object[b]:3-object[b];  
        if(object[a]<empty)  
            return object[a];  
        return empty;  
    }  
    else//容器往人  
    {  
        empty=4-object[b];  
        if(object[a]>empty)  
            return 0;  
        return object[a];  
    }  
}  
inline void move(int a,int b,int n)//转移  
{  
    object[a]-=n;  
    object[b]+=n;  
}  
bool oldposition(int c)//判断是否是曾经出现过的状态  
{  
    int i,j;  
    for(i=0;i<ptop;++i)  
    {  
        for(j=0;j<7;++j)  
            if(object[j]!=position[i][j])  
                break;  
        if(j==7)  
        {  
            if(c<position[i][7])  
            {  
                position[i][7]=c;  
                return false;  
            }  
            else 
                return true;  
        }  
    }  
    for(i=0;i<7;++i)  
        position[ptop][i]=object[i];  
    position[ptop][7]=c;  
    ++ptop;  
    return false;  
}  
void resolve(int c)  
{  
    if(object[0]==0&&object[1]==0&&object[2]==0)  
    //得到一个解,输出  
    {  
        if(c<cmin)cmin=c;  
            cout<<"第"<<(++scount)<<"个解,共"<<c<<"步实现:"<<endl;  
        for(int i=0;i<c;++i)  
            cout<<solution[i][0]<<"->"<<solution[i][1]<<":"<<solution[i][2]<<endl;  
        cout<<"-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"<<endl;  
        return;  
    }  
    if(oldposition(c))  
        return;//好汉不走回头路  
    int a,b,n;  
    for(a=0;a<3;++a)  
        for(b=0;b<7;++b)  
            if((n=pour(a,b))>0)  
            {  
                solution[c][0]=a;  
                solution[c][1]=b;  
                solution[c][2]=n;  
                move(a,b,n);  
                resolve(c+1);//下一步搜索  
                move(b,a,n);  
            }  
}  
void main()  
{  
    resolve(0);  
    cout<<"完成需要的最少步数是:"<<cmin<<endl;  

 

原创粉丝点击