洛谷P3139 [USACO16FEB] Milk Pails S

P3139 [USACO16FEB] Milk Pails S

题目描述

Farmer John 接到了一份需要立即完成的订单,要求他提供恰好 $M$ 单位的牛奶($1 \leq M \leq 200$)。不幸的是,他先进的挤奶机刚刚坏了,现在他只有两个容量为整数 $X$ 和 $Y$($1 \leq X, Y \leq 100$)的牛奶桶可以用来量取牛奶。两个桶最初都是空的。使用这两个桶,他可以执行最多 $K$ 次以下类型的操作($1 \leq K \leq 100$):

  • 他可以将任意一个桶完全装满。

  • 他可以将任意一个桶完全倒空。

  • 他可以将一个桶中的牛奶倒入另一个桶,直到前者被倒空或后者被装满(以先发生的情况为准)。

尽管 FJ 意识到他可能无法最终在两个桶中得到恰好 $M$ 单位的牛奶,但请帮助他计算 $M$ 与两个桶中牛奶总量之间的最小误差。也就是说,请计算 $|M-M'|$ 的最小值,其中 $M'$ 是 FJ 可以在两个桶中共同构造的牛奶量。

输入格式

输入的第一行也是唯一一行包含 $X$、$Y$、$K$ 和 $M$。

输出格式

输出 $M$ 与 FJ 可以生产的牛奶量之间的最小误差。

输入输出样例 #1

输入 #1

14 50 2 32

输出 #1

18

说明/提示

在两步操作中,FJ 可以在他的桶中留下以下数量的牛奶:

(0, 0) = 0 单位  
(14, 0) = 14 单位  
(0, 50) = 50 单位  
(0, 14) = 14 单位  
(14, 36) = 50 单位  
(14, 50) = 64 单位  

最接近 32 单位的是 14 单位,误差为 18。注意,要倒空第一个桶以得到 (0, 36) 需要额外的步骤。

我们来分析一下这道题,同样是用bfs算法,也可以理解为最短路径的一种,根据题目我们手里有两个瓶子用来倒入牛奶,问题是怎么样倒入使得误差最小
此时我们思考得出一共有如下六种操作方法

点击查看代码
Node next[6];
        //加满a
        next[0] = (Node){x, b, step + 1};
        // 加满b
        next[1] = (Node){a, y, step + 1};
        //倒空a
        next[2] = (Node){0, b, step + 1};
        // 倒空b
        next[3] = (Node){a, 0, step + 1};
        //把a的倒进b
        int pour = min(a, y - b);
        next[4] = (Node){a - pour, b + pour, step + 1};
        //把b的倒进a
        pour = min(b, x - a);
        next[5] = (Node){a + pour, b - pour, step + 1};
每个状态都记录下来,防止重复访问。 我们需要在不超过k次的操作中得到与m相差最小的牛奶装法

同时每次操作完都要实时更新最小的误差量

点击查看代码
int error = abs(m - total);
        if (error < min_error) {
            min_error = error;
        }
先来定义一下结构体
点击查看代码
typedef struct {
    int a, b, step;
} Node;

完整代码如下

点击查看代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXN 101

typedef struct {
    int a, b, step;
} Node;

int visited[MAXN][MAXN][MAXN];

int min(int a, int b) {
    return a < b ? a : b;
}

int BFS_milk(int x, int y, int k, int m) {
    Node queue[10000];
    int front = 0, rear = 0;
    memset(visited, 0, sizeof(visited));
    queue[rear++] = (Node){0, 0, 0};
    visited[0][0][0] = 1;

    int min_error = abs(m);  // 初始误差是 abs(m - 0)

    while (front < rear) {
        Node now = queue[front++];
        int a = now.a;
        int b = now.b;
        int step = now.step;

        int total = a + b;
        int error = abs(m - total);
        if (error < min_error) {
            min_error = error;
        }

        if (step == k)
            continue;

        Node next[6];
        //加满a
        next[0] = (Node){x, b, step + 1};
        // 加满b
        next[1] = (Node){a, y, step + 1};
        //倒空a
        next[2] = (Node){0, b, step + 1};
        // 倒空b
        next[3] = (Node){a, 0, step + 1};
        //把a的倒进b
        int pour = min(a, y - b);
        next[4] = (Node){a - pour, b + pour, step + 1};
        //把b的倒进a
        pour = min(b, x - a);
        next[5] = (Node){a + pour, b - pour, step + 1};

        for (int i = 0; i < 6; ++i) {
            int na = next[i].a;
            int nb = next[i].b;
            if (!visited[na][nb][step + 1]) {
                visited[na][nb][step + 1] = 1;
                queue[rear++] = next[i];
            }
        }
    }
    return min_error;
}

int main() {
    int X, Y, K, M;
    scanf("%d %d %d %d", &X, &Y, &K, &M);
    int result = BFS_milk(X, Y, K, M);
    printf("%d\n", result);
    return 0;

posted @ 2025-05-15 00:06  sirro1uta  阅读(32)  评论(0)    收藏  举报