题解:洛谷 P1693 Sleepy Cow Herding
【题目来源】
洛谷:P1693 [USACO19FEB] Sleepy Cow Herding B - 洛谷(luogu.com.cn)
【题目描述】
Farmer John的三头获奖奶牛Bessie、Elsie和Mildred,总是会迷路走到农场上遥远的地方去!他需要你帮助将她们一起赶回来。
农场的草地大体是一块狭长的区域——我们可以将其想象成一条数轴,奶牛可以占据数轴上的任意整数位置。这3头奶牛现在正位于不同的整数位置,Farmer John想要移动她们,使得她们占据三个相邻的位置(例如,位置\(6\)、\(7\)、\(8\))。
不幸的是,奶牛们现在很困,Farmer John要让她们集中精力听从命令移动并不容易。任意时刻,他只能使得一头处在“端点”(在所有奶牛中位置最小或最大)位置的奶牛移动。当他移动奶牛时,他可以命令她走到任意一个未被占用的整数位置,只要在新的位置上她不再是一个端点。可以看到随着时间的推移,这样的移动可以使奶牛们趋向越来越近。
请求出使得奶牛们集中到相邻位置所进行的移动次数的最小和最大可能值。
【输入】
输入包含一行,包括三个空格分隔的整数,为Bessie、Elsie和Mildred的位置。每个位置均为一个范围\(1\dots 10^9\)内的整数。
【输出】
输出的第一行包含Farmer John需要将奶牛们聚集起来所需进行的最小移动次数。第二行包含他将奶牛聚集起来能够进行的最大移动次数。
【输入样例】
4 7 9
【输出样例】
1
2
【核心思想】
-
问题分析:给定三头奶牛在数轴上的位置,每次只能移动端点位置的奶牛到任意未被占用的整数位置,且移动后该奶牛不能仍是端点。求使三头奶牛占据三个相邻位置的最小和最大移动次数。这是一个分类讨论/直接模拟问题,关键在于分析三头奶牛的相对位置间隔,根据间隔大小直接判定最小和最大步数。
-
算法选择:
- 排序 + 间隔分析:先排序得到 \(a_1 < a_2 < a_3\),计算两个间隔 \(d_1 = a_2 - a_1\)、\(d_2 = a_3 - a_2\)
- 分类讨论:根据 \((d_1, d_2)\) 的不同组合,直接输出对应的最小和最大移动次数
-
关键步骤:
- 读取输入:三个整数位置
- 排序:
sort(a+1, a+4),得到有序位置 \(a_1, a_2, a_3\) - 计算间隔:\(d_1 = a_2 - a_1\),\(d_2 = a_3 - a_2\)
- 分类判断:
- 已相邻:\(d_1 = 1\) 且 \(d_2 = 1\):最小 = 最大 = \(0\)
- 一步可达:\((d_1 = 2\) 且 \(d_2 = 1)\) 或 \((d_1 = 1\) 且 \(d_2 = 2)\) 或 \((d_1 = 2\) 且 \(d_2 = 2)\):最小 = 最大 = \(1\)
- 间隔一大一小:
- 若 \(d_1 > 2\) 且 \(d_2 = 1\) 或 \(d_1 = 1\) 且 \(d_2 > 2\):最小 = \(2\)
- 若 \(d_1 > 2\) 且 \(d_2 = 2\) 或 \(d_1 = 2\) 且 \(d_2 > 2\):最小 = \(1\)
- 最大 = \(\max(d_1 - 1, d_2 - 1)\)
- 间隔均大于 \(2\):最小 = \(2\),最大 = \(\max(d_1 - 1, d_2 - 1)\)
-
时间/空间复杂度:
- 时间复杂度:\(O(1)\),仅排序和若干次比较
- 空间复杂度:\(O(1)\),仅需存储三个位置
-
分类讨论的核心思想:
- 端点移动约束:只能移动最小或最大位置的奶牛,且移动后不能仍是端点,这意味着端点奶牛必须移动到中间区域
- 最小步数分析:
- 若存在间隔为 \(2\):可将中间奶牛或端点奶牛一步移动到间隔处,使三者连续
- 若两个间隔均 \(\ge 3\):至少需要 \(2\) 步(先移动一个端点缩小间隔,再调整)
- 最大步数分析:每次只移动一步(到相邻空位),最大步数 = 较大间隔 \(- 1\)(逐格移动端点向中间靠拢)
- 适用于"小规模约束移动 + 极值分析"类问题,核心是通过间隔分类直接得出答案
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int a[5]; // 存储三个奶牛的位置
int minn = 0; // 最小移动次数
int maxn = 0; // 最大移动次数
// 文件输入输出流(注释掉的代码)
// ifstream filein("herding.in");
// ofstream fileout("herding.out");
int main()
{
// 输入三个奶牛的位置
for (int i = 1; i <= 3; i++)
{
// cin >> a[i];
// filein >> a[i];
cin >> a[i];
}
// 对三个位置进行排序(从小到大)
sort(a + 1, a + 3 + 1);
// 情况1:三个奶牛已经连续相邻(间隔都为1)
if (a[2] - a[1] == 1 && a[3] - a[2] == 1)
{
// 无需移动
cout << 0 << endl << 0 << endl;
}
// 情况2:两个间隔分别为2和1(如:1,3,4 或 1,2,4)
else if ((a[2] - a[1] == 2 && a[3] - a[2] == 1) || (a[2] - a[1] == 1 && a[3] - a[2] == 2))
{
// 只需要移动一次即可使三个奶牛连续
cout << 1 << endl << 1 << endl;
}
// 情况3:两个间隔都为2(如:1,3,5)
else if (a[2] - a[1] == 2 && a[3] - a[2] == 2)
{
// 只需要移动一次即可使三个奶牛连续
cout << 1 << endl << 1 << endl;
}
// 情况4:一个间隔大于2,另一个间隔为1(如:1,4,5 或 1,2,5)
else if ((a[2] - a[1] > 2 && a[3] - a[2] == 1) || (a[2] - a[1] == 1 && a[3] - a[2] > 2))
{
minn = 2; // 最小需要移动2次
maxn = max(a[2] - a[1] - 1, a[3] - a[2] - 1); // 最大移动次数为较大间隔减1
cout << minn << endl << maxn << endl;
}
// 情况5:一个间隔大于2,另一个间隔为2(如:1,4,6 或 1,3,6)
else if ((a[2] - a[1] > 2 && a[3] - a[2] == 2) || (a[2] - a[1] == 2 && a[3] - a[2] > 2))
{
minn = 1; // 最小需要移动1次
maxn = max(a[2] - a[1] - 1, a[3] - a[2] - 1); // 最大移动次数为较大间隔减1
cout << minn << endl << maxn << endl;
}
// 情况6:两个间隔都大于2(如:1,5,10)
else if (a[2] - a[1] > 2 && a[3] - a[2] > 2)
{
minn = 2; // 最小需要移动2次
maxn = max(a[2] - a[1] - 1, a[3] - a[2] - 1); // 最大移动次数为较大间隔减1
cout << minn << endl << maxn << endl;
}
return 0;
}
【运行结果】
4 7 9
1
2
浙公网安备 33010602011771号