用BFS方法求解平分汽油问题

一只大桶里盛着16升汽油,要求把它们分成两份,每份9升,可是旁边没有量器, 只有一只能装9升和一只能装5升的空桶,请你利用这3只桶倒来倒去,把汽油平分开来。

cpp 复制代码
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#define MAX_ST 10000
// 容量数组
const int Cap[3] = {16, 9, 5};
// 桶名指针数组
const char *Bname[3] = {"12升桶", "9升桶", "5升桶"};
/* 结构体记录一次倒油操作:由哪个桶给哪个桶倒的油, **
* 倒了多少油, 三个桶的油量  */
typedef struct {
    int st[3];
    int pre;
    int from;
    int to;
    int amount;
} Node;
// 定义结构体数组,依次记录所有的倒油操作
Node queue[MAX_ST];
// 队首队尾标记
int head = 0, tail = 0;
// 记录三个桶油量出现与否
bool visited[17][10][6];
// 到达目标状态
bool Is_target(int s[3]);
// 一次倒油操作
bool Pour_oil(int curr[3], int A, int B, int next[3], int *amount);
// 启动倒油操作,用BFS方法寻找最短步数
int bfs(int start[3]);
// 输出平分的步骤
void Print_path(int target_index);

int main() {
    int start[3] = {16, 0, 0};  // 初始三个桶的油量 
    int result;   
    
    memset(visited, 0, sizeof(visited));
    // 记录有无解决方案
    result = bfs(start);
    
    if (result != -1) 
    {
        printf("找到最优解!\n");
        Print_path(result);
    } 
    else 
    {
        printf("未找到解决方案\n");
    }
    
    return 0;
}

bool Is_target(int s[3]) 
{
    return (s[0] == 8 && s[1] == 8 && s[2] == 0);
}

bool Pour_oil(int curr[3], int A, int B, 
              int next[3], int *amount) 
{
    int space;
    // 若倒出桶为空,或倒入桶已满,倒油操作失败
    if (curr[A] == 0 || curr[B] == Cap[B])
        return false;
    // next数组赋初值
    next[0] = curr[0];
    next[1] = curr[1];
    next[2] = curr[2];
    // 倒入桶能导入的油量
    space =Cap[B] - curr[B];
    // 确定实际的倒油量,curr[A]=倒出桶的油量
    *amount = curr[A] < space ? curr[A] : space;
    // 执行倒油操作后,倒出桶和倒入桶的油量
    next[A] -= *amount;
    next[B] += *amount;
    
    return true;    // 实现了一次倒油操作
}

int bfs(int start[3]) 
{
    int i, j;
    int next[3];
    int amount;
    
    // 三个桶的初始状态放入队尾,-1是初始标志
    queue[tail].st[0] = start[0];
    queue[tail].st[1] = start[1];
    queue[tail].st[2] = start[2];
    queue[tail].pre = -1;
    queue[tail].from = -1;
    queue[tail].to = -1;
    queue[tail].amount = 0;
    tail++;    // 新的位置索引
    // 对三个桶的状态做标记
    visited[start[0]][start[1]][start[2]] = true;
    // 若队列不空
    while (head < tail) 
    {
        // 从队首取出结构体
        Node *curr = &queue[head];
        
        if (Is_target(curr->st)) 
        {
            return head;   // 返回目标状态的索引值
        }
        // 由当前三个桶的状态尝试各种允许的倒油操作
        for (i = 0; i < 3; i++) 
        {
            for (j = 0; j < 3; j++) 
            {
                if (i == j) 
                    continue;
                
                if (Pour_oil(curr->st, i, j, next, &amount)) 
                {
                    if (!visited[next[0]][next[1]][next[2]]) 
                    {
                        // 一次倒油操作入队尾
                        queue[tail].st[0] = next[0];
                        queue[tail].st[1] = next[1];
                        queue[tail].st[2] = next[2];
                        queue[tail].pre = head;
                        queue[tail].from = i;
                        queue[tail].to = j;
                        queue[tail].amount = amount;
                        tail++;
                        // 对三个桶的状态做标记
                        visited[next[0]][next[1]][next[2]] = true;
                    }
                }
            }
        }
        
        head++;   // 新的队首索引
    }
    
    return -1;   // 没有找到解决方案
}

void Print_path(int target_index) 
{
    int path[MAX_ST];
    int len = 0;
    int i;
    
    i = target_index;    
    // path逆序存放求解步骤索引
    while (i != -1) 
    {
        path[len++] = i;    // 存索引i
        i = queue[i].pre;   // 由索引i获得其前驱索引 
    }
    
    printf("共 %d 步操作:\n", len - 1);
    // 逆序输出求解步骤
    for (i = len - 1; i >= 0; i--) 
    {
        Node *node = &queue[path[i]];  // 指向状态结构体
        
        if (node->from == -1) 
        {
            printf("初始状态: [16升=%d, 9升=%d, 5升=%d]\n",
                   node->st[0], node->st[1], node->st[2]);
        } 
        else 
        {
            printf("步骤 %d: 从%s倒%d升到%s\n", len - 1 - i,
                   Bname[node->from], node->amount, 
                   Bname[node->to]);
            printf("         [16升=%d, 9升=%d, 5升=%d]\n",
                   node->st[0], node->st[1], 
                   node->st[2]);
        }
    }
}

找到最优解!

共 7 步操作:

初始状态: 16升=16, 9升=0, 5升=0

步骤 1: 从12升桶倒9升到9升桶

16升=7, 9升=9, 5升=0

步骤 2: 从9升桶倒5升到5升桶

16升=7, 9升=4, 5升=5

步骤 3: 从5升桶倒5升到12升桶

16升=12, 9升=4, 5升=0

步骤 4: 从9升桶倒4升到5升桶

16升=12, 9升=0, 5升=4

步骤 5: 从12升桶倒9升到9升桶

16升=3, 9升=9, 5升=4

步骤 6: 从9升桶倒1升到5升桶

16升=3, 9升=8, 5升=5

步骤 7: 从5升桶倒5升到12升桶

16升=8, 9升=8, 5升=0

相关推荐
To_OC9 小时前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
金銀銅鐵13 小时前
[Python] 扩展欧几里得算法
python·数学·算法
To_OC15 小时前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC1 天前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
05Kevin2 天前
lk每日冒险题--数据结构6.27
算法
To_OC2 天前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安3 天前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者3 天前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent