用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

相关推荐
203号居民1 小时前
LeetCode hot 100 —41. 缺失的第一个正数
数据结构·算法·leetcode
专注仿真3 小时前
问答大模型技术方案算法实现-熵权法融合算法 + 交叉编码器重排算法
人工智能·python·算法·语言模型·问答大模型关键算法·熵权融合算法·交叉编码器重排算法
地平线开发者4 小时前
量化为什么会有损失:从量化误差到精度下降
算法
手写码匠5 小时前
华为云Flexus+DeepSeek征文|Dify 多 Agent 会话管理与上下文工程实战:让智能体“记住该记住的,忘掉该忘掉的“
人工智能·深度学习·算法·aigc
2401_862880825 小时前
数据结构 --- 栈
c语言·数据结构·算法
致Great5 小时前
Qwen3.8-27B 接入 DSH 全流程实测:写代码、跑长任务,都没问题
算法
Tisfy6 小时前
LeetCode 3471.找出最大的几近缺失整数:三种情况判断
算法·leetcode·题解·分类讨论
linux-hzh6 小时前
百日算法修炼 · Day 12
算法·深度优先
额额额对了7 小时前
数据结构:二叉树
c语言·数据结构·算法
iNeuOS工业互联网7 小时前
iNeuOS_Vision_视觉分析,增加SAM3模型对实例分割和目标检测AI自动标注图片样本功能
人工智能·算法·目标检测