HDU 1495 非常可乐

非常可乐

  大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

Input

  三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。Output如果能平分的话请输出最少要倒的次数,否则输出"NO"。

Sample Input

7 4 3
4 1 3
0 0 0

Sample Output

NO
3

解题思路:
  本题给出多组数据,每组数据包括3个整数可乐体积s,第一个杯子容量n, 另一个杯子容量m。s == n  + m 要求输出想要平分可乐最少倒的次数。

  这里本题使用BFS,广搜的基本思路是,用一个队列,搜索下一步可以抵达的所有状态加入队列,直到符合条件或队列为空为止。

  记录初始状态为可乐瓶中可乐体积为s, 两个杯中的可乐体积都为0, 当前状态若想抵达下一个状态有s向n倒水、s向m倒水、n向s倒水、n向m倒水、m向s倒水、m向n倒水这6种方法,执行完这6种方法后得到的6种状态即为当前状态的下一状态。将当前状态出队寻找下一状态,取所有合法状态加入队列,继续向下搜索即可。

  结束条件,由于s == n + m所以若想将可乐分为两相等份,最终的情况一定是,可乐瓶与较大的杯中各有一半可乐,较小的杯中没有可乐。这里可以使用一个数组v[3],v[0] v[1] v[2]分别记录可乐瓶, 较大的杯子,与较小的杯子的容积。用一个结构体node记录每个状态各容器内的可乐体积与当前状态的倒水次数,布尔数组vis记录状态是否出现过。

  

struct node{
    int v[3];   
    //v[]0 v[1] v[2] 分别为当前状态可乐瓶、大杯、小杯之内的可乐体积
    int step;
    //step记录倒水次数
}Node;
bool vis[maxn][maxn][maxn] = {false};
//记录状态是否出现过

 

  倒水,可以写一个判断倒水函数,传入对应容器x与y,执行x向y倒水,如果被倒入容器y的容积小于两个容器中可乐总体积,就将容器y倒满,如果y的容积大于两容器中的可乐总体积 ,就将所有可乐倒入y容器,两容器中可乐总体积减去y中的可乐体积就是x中剩余的可乐体积。

  

void judge(int x, int y){   //传入容器x y
    int sum = Node.v[x] + Node.v[y];//记录两容器中的可乐总体积
    if(v[y] <= sum){    //y的容积小于两个容器中可乐总体积
        Node.v[y] = v[y];   //将容器y倒满
    }else{  //y的容积大于两容器中的可乐总体积
        Node.v[y] = sum;    //将所有可乐倒入y
    }
    Node.v[x] = sum - Node.v[y];    
    //两容器中可乐总体积减去y中的可乐体积,x中剩余的可乐体积
}
judge

  AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
struct node{
    int v[3];   
    //v[]0 v[1] v[2] 分别为当前状态可乐瓶、大杯、小杯之内的可乐体积
    int step;
    //step记录倒水次数
}Node;
bool vis[maxn][maxn][maxn] = {false};
//记录状态是否出现过
int v[3];
void judge(int x, int y){   //传入容器x y
    int sum = Node.v[x] + Node.v[y];//记录两容器中的可乐总体积
    if(v[y] <= sum){    //y的容积小于两个容器中可乐总体积
        Node.v[y] = v[y];   //将容器y倒满
    }else{  //y的容积大于两容器中的可乐总体积
        Node.v[y] = sum;    //将所有可乐倒入y
    }
    Node.v[x] = sum - Node.v[y];    
    //两容器中可乐总体积减去y中的可乐体积,x中剩余的可乐体积
}
void BFS(){
    Node.v[0] = v[0];   
    Node.v[1] = 0;
    Node.v[2] = 0;
    Node.step = 0;
    //记录初始状态,可乐瓶中体积为s,两个杯中的可乐体积都为0
    memset(vis, false, sizeof(vis));
    //初始化所有状态为未出现过
    vis[Node.v[0]][Node.v[1]][Node.v[2]] = true;
    //标记当前状态为已出现
    queue<node> Q;
    //队列Q记录当前可操作状态
    Q.push(Node);
    //当前状态入队
    while(!Q.empty()){  
        node top = Q.front();
        //取出队首元素
        if(top.v[0] == top.v[1] && top.v[2] == 0){
            //可乐瓶与较大的杯中各有一半可乐,较小的杯中没有可乐
            printf("%d\n", top.step);//输出次数
            return;
        }
        Q.pop();
        
        for(int i = 0; i < 3; i++){ //搜索所有6种状态
            for(int j = 0; j < 3; j++){
                if(i != j){ //自己不能向自己倒水
                    Node = top;
                    judge(i , j);   //执行i向j倒水
                    if(!vis[Node.v[0]][Node.v[1]][Node.v[2]]){  //若新状态之前没有出现过
                        Node.step++;
                        //倒水次数加一
                        vis[Node.v[0]][Node.v[1]][Node.v[2]] = true;
                        //标记新状态为已出现
                        Q.push(Node);
                        //新状态入队 
                    }
                }
            }
        }
    }
    printf("NO\n"); //若不能分成两份输出NO
}
int main()
{
    int s, n, m;
    while(scanf("%d%d%d", &s, &n, &m) != EOF && s || n || m){   //输入s n m 以0为结束标志
        v[0] = s;
        //获得大杯与小杯的容积
        if(n > m){
            v[1] = n;
            v[2] = m;
        }else{
            v[1] = m;
            v[2] = n;
        }
        BFS();
    }
    return 0;
}

 

  

posted @ 2018-12-07 14:57  suvvm  阅读(372)  评论(0编辑  收藏  举报