BFS(两个水杯转换水量)

使用BFS算法寻找两个水杯之间转换水量的最短路径。操作包括:填充、倒空和倒入。问题类似于1426题,每个状态的子节点数量为6,详细步骤参考1426题解。
题意:给出两个容积分别为 a 和 b 的pot,按照以下三种操作方式,求出能否在一定步数后,使者两个pot的其中一个的水量为c。

      1.FILL(i):将ipot倒满水。

      2.DROP(i):将ipot倒空水。

      3.POUR(i,j): 将ipot的水倒到jpot上,直至要么ipot为空,要么jpot为满。

 

思路:bfs求最短路径,与1426类似,只是每个节点的子节点数为6个而已。具体参照1426。

//Memory Time
//232K   32MS 

#include<iostream>
#include<string>
#include<sstream>
#include<map>
using namespace std;

int v1,v2;  //两个瓶子的容量
int c;      //目标残余水量

int k1,k2;  //在某状态时两个瓶子的剩余水量,temporary

typedef class
{
    public:
        int x,y; //当前状态(两个瓶子中的水量)
        int pos;  //记录前一状态在队列queue中的下标
        int step;  //当前步数
}process;

//把整数a、b整合为 "a,b"  的字符串形式(不包括引号),用于标记状态
string combAB(int a,int b)
{
    string s;
    ostringstream oss;

    oss<<a;
    oss<<',';
    oss<<b;

    s=oss.str();
    oss.str("");  //清空oss对象内所存储的流

    return s;
}

void fill(int i)
{
    switch(i)
    {
        case 1: {k1=v1; return;}
        case 2: {k2=v2; return;}
    }
}

void drop(int i)
{
    switch(i)
    {
        case 1: {k1=0; return;}
        case 2: {k2=0; return;}
    }
}

void pour(int i,int j)
{
    switch(i)
    {
        case 1:   //  v1 to v2
            {
                if(k1+k2<=v2)
                {
                    k2=k1+k2;
                    k1=0;
                }
                else
                {
                    k1=k1+k2-v2;
                    k2=v2;
                }
                return;
            }
        case 2:   //  v2 to v1
            {
                if(k1+k2<=v1)
                {
                    k1=k1+k2;
                    k2=0;
                }
                else
                {
                    k2=k1+k2-v1;
                    k1=v1;
                }
                return;
            }
    }
}

void BFS(void)
{
    int operation[1000]={0}; //当前步的操作: 1z0:清空z瓶子  2z0:装满z瓶子  3xy:从x瓶倒向y瓶

    map<string,bool>vist;
    vist["0,0"]=true;

    process queue[1000];  //状态队列
    int head,tail;
    queue[head=0].x=0;
    queue[tail=0].y=0;
    queue[tail++].step=0;

    string ts;  //temporary
    while(head<tail)
    {
        process p=queue[head];

        if(p.x==c || p.y==c)   //得到要求的剩余水量c
        {
            cout<<p.step<<endl;

            /*下标回溯,输出操作过程*/

            int ps=p.step;
            int* steps=new int[ps+1];  //从1到p.step顺序记录各步操作的下标,不使用steps[0]

            steps[ps--]=tail-1;
            while(ps)
            {
                steps[ps]=queue[ steps[ps+1] ].pos;
                ps--;
            }

            for(int i=1;i<=p.step;i++)
            {
                int temp=operation[ steps[i]-1 ];  //注意各个数组间的下标关系

                switch(temp/100)
                {
                    case 1:
                        {
                            cout<<"DROP("<<(temp/10)%10<<')'<<endl;
                            break;
                        }
                    case 2:
                        {
                            cout<<"FILL("<<(temp/10)%10<<')'<<endl;
                            break;
                        }
                    case 3:
                        {
                            cout<<"POUR("<<(temp/10)%10<<','<<temp%10<<')'<<endl;
                            break;
                        }
                }
            }

            delete steps;

            return;
        }

        /*装满v1*/

        k1=p.x;
        k2=p.y;
        fill(1);
        ts=combAB(k1,k2);
        if(!vist[ts])
        {
            vist[ts]=true;
            queue[tail].x=k1;
            queue[tail].y=k2;
            queue[tail].step=p.step+1;  //当前的操作步数
            queue[tail].pos=head;      //前一步操作在queue[]中的下标
            operation[tail++]=210;     //当前的操作
        }

        /*装满v2*/

        k1=p.x;
        k2=p.y;
        fill(2);
        ts=combAB(k1,k2);
        if(!vist[ts])
        {
            vist[ts]=true;
            queue[tail].x=k1;
            queue[tail].y=k2;
            queue[tail].step=p.step+1;
            queue[tail].pos=head;
            operation[tail++]=220;
        }

        /*清空v1*/

        k1=p.x;
        k2=p.y;
        drop(1);
        ts=combAB(k1,k2);
        if(!vist[ts])
        {
            vist[ts]=true;
            queue[tail].x=k1;
            queue[tail].y=k2;
            queue[tail].step=p.step+1;
            queue[tail].pos=head;
            operation[tail++]=110;
        }

        /*清空v2*/

        k1=p.x;
        k2=p.y;
        drop(2);
        ts=combAB(k1,k2);
        if(!vist[ts])
        {
            vist[ts]=true;
            queue[tail].x=k1;
            queue[tail].y=k2;
            queue[tail].step=p.step+1;
            queue[tail].pos=head;
            operation[tail++]=120;
        }

        /*v1倒向v2*/

        k1=p.x;
        k2=p.y;
        pour(1,2);
        ts=combAB(k1,k2);
        if(!vist[ts])
        {
            vist[ts]=true;
            queue[tail].x=k1;
            queue[tail].y=k2;
            queue[tail].step=p.step+1;
            queue[tail].pos=head;
            operation[tail++]=312;
        }

        /*v2倒向v1*/

        k1=p.x;
        k2=p.y;
        pour(2,1);
        ts=combAB(k1,k2);
        if(!vist[ts])
        {
            vist[ts]=true;
            queue[tail].x=k1;
            queue[tail].y=k2;
            queue[tail].step=p.step+1;
            queue[tail].pos=head;
            operation[tail++]=321;
        }

        head++;
    }

    cout<<"impossible"<<endl;
    return;
}

int main(void)
{
    while(cin>>v1>>v2>>c)
        BFS();
    return 0;
}
View Code

 

### 倒水问题的解决方案 倒水问题是经典的数学与算法问题之一,其核心在于通过一系列操作找到满足特定条件的状态。以下是针对 `m=3`、`n=5` `d=4` 的具体实方法。 #### 裴蜀定理的应用 根据裴蜀定理,方程 \( ax + by = d \) 有整数解当且仅当 \( d \) 是 \( x \) \( y \)大公约数 (gcd) 的倍数[^1]。因此,在本例中: \[ \text{gcd}(3, 5) = 1,\quad \text{由于} \, 4 \% 1 = 0,\, \text{故存在整数解} \] 这意味着可以通过若干次舀水倒水的操作得到恰好 4 升水。 --- #### 广度优先搜索 (BFS)路径寻找 为了找出具体的实路径,可以采用广度优先搜索 (BFS) 来模拟所有的状态转移过程。每种状态可以用 `(x, y)` 表示,分别代表两容器内的水量。初始状态为 `(0, 0)`,目标状态为其中一个容器含有 4 升水。 ##### BFS 状态转移规则 定义以下六种基本操作: 1. 将第一个容器填满:\( x' = m \), \( y' = y \) 2. 将第二个容器填满:\( x' = x \), \( y' = n \) 3. 清空第一个容器:\( x' = 0 \), \( y' = y \) 4. 清空第二个容器:\( x' = x \), \( y' = 0 \) 5. 把第一个容器倒入第二个容器直到第二个容器满或者第一个容器为空为止。 6. 把第二个容器倒入第一个容器直到第一个容器满或者第二个容器为空为止。 这些操作构成了状态之间的转换关系。 --- #### Python 实代码 下面是一个基于 BFS 的程序来计算所有可能的解法及其详细步骤: ```python from collections import deque def find_water_paths(m, n, d): if d > max(m, n): # 如果目标量大于任一桶容量,则无解 return [] visited = set() queue = deque([(0, 0, [])]) solutions = [] while queue: current_x, current_y, path = queue.popleft() if current_x == d or current_y == d: # 找到目标状态 solutions.append(path) # 枚举所有可能的操作并加入队列 next_states = [ ("Fill A", (m, current_y)), # Fill first container to full capacity. ("Fill B", (current_x, n)), # Fill second container to full capacity. ("Empty A", (0, current_y)), # Empty the first container completely. ("Empty B", (current_x, 0)), # Empty the second container completely. ( "Pour A->B", pour(current_x, current_y, m, n), ), # Pour from A into B until B is full or A becomes empty. ( "Pour B->A", pour_reverse(current_x, current_y, m, n), ) # Pour from B into A until A is full or B becomes empty. ] for action, state in next_states: if state not in visited: visited.add(state) new_path = path.copy() new_path.append((action, state)) queue.append((*state, new_path)) return solutions def pour(x, y, cap_a, cap_b): """Simulate pouring water from bucket A to bucket B.""" transfer = min(x, cap_b - y) return (x - transfer, y + transfer) def pour_reverse(x, y, cap_a, cap_b): """Simulate pouring water from bucket B to bucket A.""" transfer = min(y, cap_a - x) return (x + transfer, y - transfer) # Example usage if __name__ == "__main__": m, n, d = 3, 5, 4 all_solutions = find_water_paths(m, n, d) for idx, solution in enumerate(all_solutions, start=1): print(f"Solution {idx}:") for step in solution: print(step) print("-" * 40) ``` --- #### 输出解释 运行以上代码后,将返回多个可行方案以及对应的步骤列表。例如,对于输入参数 `m=3`, `n=5`, `d=4`,可能会输出如下结果(仅为示意): ``` Solution 1: ('Fill B', (0, 5)) ('Pour B->A', (3, 2)) ('Empty A', (0, 2)) ('Pour B->A', (2, 0)) ('Fill B', (2, 5)) ('Pour B->A', (3, 4)) ---------------------------------------- Solution 2: ... ``` 每一组解都描述了一条从初始状态到达目标状态的有效路径。 --- ### 性能优化建议 如果需要处理更大的数值范围或更复杂的约束条件,可进一步引入剪枝策略减少不必要的状态探索。例如: - 提前判断是否存在理论上的可能性; - 避免重复访问相同状态。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值