标题:算法倒水问题,求教高手。
取消只看楼主
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
结帖率:95.65%
已结贴  问题点数:20 回复次数:7 
算法倒水问题,求教高手。
好久没来,有时真想休学一年,好好学学算法,离散数学等。
今天来请交大神们,是在算法书上看到的。

题目是:
有装满水的6升的杯子、空的3升杯子和1升杯子,3个杯子中都没有刻度。在不使用其他道具的情况下,是否可以量出4升的水呢?倒水问题的一种方法为:
                      (6,0,0)->(3,3,0)->(3,2,1)->(4,2,0)
   注意:由于没有刻度,用杯子x给杯子y倒水时必须一直把杯子y倒满或者把杯子x倒空,而不能中途停止。
你的任务是解决一般性的问题:设大、中、小3个杯子的容量分别为a,b,c,最初只有大杯子装满水,其他两个杯子为空。最少需要多少步才能让某一个杯子中的水有x升呢?(0<c<b<a<1000)。

Input
   输入数据包括多组数据,每组数据占一行,分别为a,b,c,x,四个数间用一个空格隔开。

Output
   每行输入对应一行输出,即完成目标所需的最小步数。如果不能完成目标,输出"NO"。

Sample Input
6 3 1 4
9 6 3 1

Sample Output
3
NO



我的基本思路是,建立的模型是个树形结构,从头结点开始状态为(a,0,0)。然后用队列按照层拓展下去,这样应该可以求出最少步骤吧?
但是书上分析的是图模型,而且有点不明白边数不超过6*10^6是怎么估算出来的,我只知道结点数是不会超过10^6??求解~
搜索更多相关主题的帖子: 杯子 离散数学 
2013-03-13 21:49
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-13 21:58
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 
回复 3楼 beyondyf
谢谢B版了,搜到了。先保存下来。大概浏览下,好简短,觉得思路应该特别清晰,佩服你的功力啊,明天再看,小菜我自己先写写。

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-13 23:52
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 
回复 7楼 beyondyf
https://bbs.bccn.net/viewthread.php?tid=359744&extra=&page=2

不过B版的代码无法编译过啊~。

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-15 20:46
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 
回复 3楼 beyondyf
b版什么时候在线啊,我把自己写的代码贴出来,能不能赐教。加了大量注释了,不知道有还有没有什么错。

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-15 21:51
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 
回复 11楼 beyondyf
#include <stdio.h>
typedef struct Node{
    int have_v[3];//状态的水量
    int size_v[3];//对应瓶子的容量
    int step;
    int father;
}Node;
typedef struct dumpWaterTable{
    int v[2];
}dumpWaterTable;//倒水表单元结构
dumpWaterTable table_dump_methods[6] = {{0,1},{0,2},{1,0},{1,2},{2,0},{2,1}};//倒水方法表
Node queue[100000];//存储各个状态量的队列
int front,rear;
void Init(int &a, int &b, int &c, Node & root,bool flag);//初始化搜索根结点
bool bfs(Node & root,int x);//宽度优先搜素
void Print_L(int index,int now);//按照找到的结点,递归打印倒水过程
bool isequal(Node &a,int &b);//判断是否出现合法的且有水杯的水等于b
bool isvalid(int i,Node v,Node & dumped);//判断倒水表中第i中方法是否合适
int min(int a, int b);//返回最小值
//void testout(Node & v);
int a,b,c;
int main(){
    int x;
    while(scanf("%d%d%d%d",&a,&b,&c,&x)!=EOF){//输入各个瓶子的容量,已经目标容量的为x
        Node root;//根结点
        Init(a,b,c,root,true);//根结点初始化
        front = rear =  0;
        bfs(root,x);//开始搜索
    }
    return 0;
}

void Print_L(int index,int now){//递归打印
    int key = queue[index].step;
    int fa = queue[index].father;
    if(key != 0){
        Print_L(fa,now + 1);
    }
    if(index == 0)
        printf("至少需要%d步\n",now);
    printf("(%d,%d,%d)\n",queue[key].have_v[0],queue[key].have_v[1],queue[key].have_v[2]);
}

void Init(int &a, int &b, int &c, Node & root,bool flag){
    if(flag){
        root.have_v[0] = a;
        root.have_v[1] = 0;
        root.have_v[2] = 0;
        root.size_v[0] = a;
        root.size_v[1] = b;
        root.size_v[2] = c;
        root.step = root.father= 0;
    }
    else{
        root.size_v[0] = a;
        root.size_v[1] = b;
        root.size_v[2] = c;
    }
}

bool bfs(Node & root,int x){
    queue[rear++] = root;//根结点入队列
    int step = 0;
    bool Have_Find = false;//没发现目标结点
    while(front < rear){
        Node v = queue[front++];//出队列  
        if(isequal(v,x)){
            Have_Find = true;
            break;
        }
        for(int i = 0; i < 6; i++){//尝试各种倒水法,合法就拓展入队列
            Node dumped;
            Init(a,b,c,dumped,false);
            if(isvalid(i,v,dumped)){
                dumped.step = ++step;
                dumped.father = v.step;
                queue[rear++] = dumped;
            }
        }
    }
    if(Have_Find){
        Print_L(front - 1,0);
    }
    else{
        printf("亲,无解\n");
    }
}
bool isequal(Node &a,int & b){
    for(int i =0; i < 3 ;i++){
        if(a.have_v[i] == b)
            return true;
    }
    return false;
}
bool isvalid(int i,Node v,Node & dumped){
    dumpWaterTable m = table_dump_methods[i];//取得倒水表第i中方式,m为方式变量,m.v[0]倒到m.v[1]
    if(v.have_v[m.v[0]] != 0 && v.have_v[m.v[1]] != v.size_v[m.v[1]]){//可倒水
        int d = min(v.have_v[m.v[0]],v.size_v[m.v[1]] - v.have_v[m.v[1]]);
        dumped.have_v[m.v[0]] = v.have_v[m.v[0]] - d;
        dumped.have_v[m.v[1]] = v.have_v[m.v[1]] + d;
        int j;
        for(j = 0; j < 3; j++){//搜索没有参与倒水的瓶子
            if(j != m.v[0] && j != m.v[1])
                break;
        }
        dumped.have_v[j] = v.have_v[j];//保存没参与倒水的瓶子
        return true;//返回合法该方法合法,并且已经执行。
    }
    return false;
}
int min(int a,int b){
    return a < b ? a : b;
}

//void testout(Node &v){
//    printf("testing(%d,%d,%d)\n",v.have_v[0],v.have_v[1],v.have_v[2]);
//}

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-15 23:21
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 
回复 11楼 beyondyf
程序代码:
#include <stdio.h>
typedef struct Node{
    int have_v[3];//状态的水量 
    int size_v[3];//对应瓶子的容量 
    int step;
    int father;
}Node;
typedef struct dumpWaterTable{
    int v[2]; 
}dumpWaterTable;//倒水表单元结构 
dumpWaterTable table_dump_methods[6] = {{0,1},{0,2},{1,0},{1,2},{2,0},{2,1}};//倒水方法表 
Node queue[100000];//存储各个状态量的队列 
int front,rear;
void Init(int &a, int &b, int &c, Node & root,bool flag);//初始化搜索根结点 
bool bfs(Node & root,int x);//宽度优先搜素 
void Print_L(int index,int now);//按照找到的结点,递归打印倒水过程 
bool isequal(Node &a,int &b);//判断是否出现合法的且有水杯的水等于b 
bool isvalid(int i,Node v,Node & dumped);//判断倒水表中第i中方法是否合适 
int min(int a, int b);//返回最小值 
//void testout(Node & v);
int a,b,c;
int main(){ 
    int x;
    while(scanf("%d%d%d%d",&a,&b,&c,&x)!=EOF){//输入各个瓶子的容量,已经目标容量的为x 
        Node root;//根结点 
        Init(a,b,c,root,true);//根结点初始化 
        front = rear =  0;
        bfs(root,x);//开始搜索 
    }
    return 0;
}

void Print_L(int index,int now){//递归打印 
    int key = queue[index].step;
    int fa = queue[index].father;
    if(key != 0){
        Print_L(fa,now + 1);
    }
    if(index == 0)
        printf("至少需要%d步\n",now);
    printf("(%d,%d,%d)\n",queue[key].have_v[0],queue[key].have_v[1],queue[key].have_v[2]);
}

void Init(int &a, int &b, int &c, Node & root,bool flag){
    if(flag){
        root.have_v[0] = a;
        root.have_v[1] = 0;
        root.have_v[2] = 0;
        root.size_v[0] = a; 
        root.size_v[1] = b;
        root.size_v[2] = c;
        root.step = root.father= 0;
    }
    else{
        root.size_v[0] = a;
        root.size_v[1] = b;
        root.size_v[2] = c;
    }
}

bool bfs(Node & root,int x){
    queue[rear++] = root;//根结点入队列 
    int step = 0;
    bool Have_Find = false;//没发现目标结点 
    while(front < rear){
        Node v = queue[front++];//出队列  
        if(isequal(v,x)){
            Have_Find = true;
            break;
        }
        for(int i = 0; i < 6; i++){//尝试各种倒水法,合法就拓展入队列 
            Node dumped;
            Init(a,b,c,dumped,false);
            if(isvalid(i,v,dumped)){
                dumped.step = ++step;
                dumped.father = v.step; 
                queue[rear++] = dumped;
            }
        }
    }
    if(Have_Find){
        Print_L(front - 1,0);
    }
    else{
        printf("亲,无解\n");
    }
}
bool isequal(Node &a,int & b){
    for(int i =0; i < 3 ;i++){
        if(a.have_v[i] == b)
            return true;
    }
    return false;
}
bool isvalid(int i,Node v,Node & dumped){
    dumpWaterTable m = table_dump_methods[i];//取得倒水表第i中方式,m为方式变量,m.v[0]倒到m.v[1] 
    if(v.have_v[m.v[0]] != 0 && v.have_v[m.v[1]] != v.size_v[m.v[1]]){//可倒水 
        int d = min(v.have_v[m.v[0]],v.size_v[m.v[1]] - v.have_v[m.v[1]]); 
        dumped.have_v[m.v[0]] = v.have_v[m.v[0]] - d;
        dumped.have_v[m.v[1]] = v.have_v[m.v[1]] + d;
        int j;
        for(j = 0; j < 3; j++){//搜索没有参与倒水的瓶子 
            if(j != m.v[0] && j != m.v[1])
                break;
        }
        dumped.have_v[j] = v.have_v[j];//保存没参与倒水的瓶子 
        return true;//返回合法该方法合法,并且已经执行。 
    }
    return false;
}
int min(int a,int b){
    return a < b ? a : b;
}

//void testout(Node &v){
//    printf("testing(%d,%d,%d)\n",v.have_v[0],v.have_v[1],v.have_v[2]);
//}

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-15 23:22
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
得分:0 
回复 11楼 beyondyf
对啦,顺便问一下B版,8数码问题,模型是图的最短路~~~,这个是这么看的~~~,小菜不懂,B版能否提醒一二。。。

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-15 23:34



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-400713-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.211740 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved