用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

相关推荐
AI科技星1 小时前
全域数学·72分册·射影原本 无穷维射影几何卷细化子目录【乖乖数学】
人工智能·线性代数·算法·机器学习·数学建模·数据挖掘·量子计算
风落无尘1 小时前
《智能重生:从垃圾堆到AI工程师》——第四章 变化的艺术
人工智能·线性代数·算法
JAVA面经实录9172 小时前
计算机基础(完整版·超详细可背诵)
java·linux·数据结构·算法
AC赳赳老秦2 小时前
知识产权辅助:用 OpenClaw 批量生成专利交底书 / 软著申请材料,自动校验格式与内容合规性
java·人工智能·python·算法·elasticsearch·deepseek·openclaw
WBluuue2 小时前
Codeforces 1093 Div2(ABCD1D2)
c++·算法
浅念-2 小时前
「一文吃透 BFS:从层序遍历到锯齿形、最大宽度、每层最大值」
数据结构·算法
汉克老师2 小时前
GESP5级C++考试语法知识(十三、贪心算法(一))
算法·贪心算法·海盗船·gesp5级·gesp五级·排队接水
梦想画家3 小时前
Apache AGE实战指南:从Cypher语法到核心图算法
算法·cypher·apache age
刀法如飞3 小时前
Go数组去重的20种实现方式,AI时代解决问题的不同思路
后端·算法·go