竞赛题目-【USACO TRAINING】母亲的牛奶

【USACO TRAINING】母亲的牛奶


题目描述:
农民约翰有三个容量分别是A,B,C升的桶,A、B、C分别是三个从1到20的整数。
最初,A和B桶都是空的,而C桶是装满牛奶的。
有时,约翰把牛奶从一个桶倒到另一个桶中,直到被灌桶装满或原桶空了。由于节约,牛奶不会有丢失。
写一个程序去帮助约翰找出当A桶是空的时候,C桶中牛奶所剩量的所有可能性。

输入:
第1行:3个整数A,B和C。

输出:
第1行:升序地列出当A桶是空的时候,C桶牛奶所剩量的所有可能性。

样例输入:
2 5 10

样例输出:
5 6 7 8 9 10


题目分析:
这道题是搜索的一道经典题——深度优先搜索和广度优先搜索都可以完成,这里作者为大家介绍广度优先搜索的做法。与一般的广度优先搜索不同,每一次搜索都有两个自变化的变量——从a桶倒入b桶,因此我们要用两个for循环。注意此时的a和b不能相等 否则就会出现无限循环的危险。
从a桶倒入b桶有两种情况:a桶全部倒空;b桶已经装满(有时将会出现a桶倒完,同时b桶也装满的情况,算入情况1)。若此时为情况1,则不需考虑溢出(超出最大容量),否则b桶就装满,a桶有剩余。由题目描述得:“A和B桶都是空的,而C桶是装满牛奶的”,即牛奶的总量总是c桶的初始值,因此我们可以 用c桶的初始值减去现在a,b桶的值得到现在c桶的值
得到此时a,b,c桶的值后,需要判断是否重复,可以使用一个二维的bool数组,用类似于“桶排序”的方式储存当前a,b桶的值(c桶无需储存,可以通过a,b桶求得)。若重复,则不储存,否则列入bool数组中,并将其入队。
一直循环到所有的情况完毕,再遍历bool数组中所有的a桶为零的元素([0][b桶]),若出现过(true),则输出。注意b桶的值需要从大到小循环(保证c桶的值从小到大输出)


程序样例:

#include<cstdio>
#include<queue> //C++特有队列
#include<algorithm>
using namespace std;
int barrel[3];bool can[25][25];
//a,b,c桶的初值 ; 判断是否重复的数组
struct BARREL{int A_barrel,B_barrel;}now; //保存a,b桶的值
queue<BARREL> que; //建立FIFO队列
int main()
{
    scanf("%d%d%d",&barrel[0],&barrel[1],&barrel[2]);
    now.A_barrel=now.B_barrel=0;
    que.push(now); //初始化队列
    while(!que.empty()) //一直循环到队列为空
    {
        for(int i=0;i<3;i++) //将i桶倒入j桶
            for(int j=0;j<3;j++)
            {
                if(i==j) continue; //避免重复
                int now_barrel[3]; //当前a,b,c桶
                now_barrel[0]=que.front().A_barrel;
                now_barrel[1]=que.front().B_barrel;
                //a,b桶初始值
                now_barrel[2]=barrel[2]-now_barrel[0]-now_barrel[1]; //c桶=总量(c桶初始值)-a桶-b桶
                int pour=now_barrel[i]+now_barrel[j]; //i桶和j桶的总和
                now_barrel[j]=min(pour,barrel[j]); //避免j桶溢出
                now_barrel[i]=pour-now_barrel[j]; //求出i桶剩余
                if(!can[now_barrel[0]][now_barrel[1]]) //判断重复
                {
                    can[now_barrel[0]][now_barrel[1]]=true;
                    now.A_barrel=now_barrel[0];
                    now.B_barrel=now_barrel[1];
                    que.push(now); //入队
                }
            }
        que.pop(); //更新队首
    }
    bool first=true;
    for(int i=20;i>=0;i--) //从大到小枚举b桶
        if(can[0][i]) //此情况出现过
        {
            first? first=false:printf(" ");
            printf("%d",barrel[2]-i); //输出
        }
    return 0;
}
/**************************************************************
    Language: C++
    Result: 正确
    Time:0 ms
    Memory:1052 kb
***************************************************************/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值