题解:洛谷 P2907 Roads Around The Farm
【题目来源】
洛谷:P2907 [USACO08OPEN] Roads Around The Farm S - 洛谷
【题目描述】
Farmer John 的奶牛对探索农场周围的领地产生了兴趣。最初,所有 \(N\) 头奶牛(\(1 \leq N \leq 10^9\))以一个大群体的形式开始沿着一条道路旅行。当遇到岔路时,群体有时会选择分成两个较小的(非空)群体,每个群体沿着一条道路继续前进。当其中一个群体到达另一个岔路时,它可能会再次分裂,依此类推。
奶牛们设计了一种特殊的分裂方式:如果它们可以分裂成两个群体,使得群体的大小正好相差 \(K\)(\(1 \leq K \leq 1000\)),那么它们就会以这种方式分裂;否则,它们就停止探索,开始安静地吃草。
假设总是会有新的岔路,计算最终安静吃草的奶牛群的数量。
【输入】
两个空格分隔的整数,\(N\) 和 \(K\)。
【输出】
一个整数,表示安静吃草的奶牛群的数量。
【输入样例】
6 2
【输出样例 】
3
【核心思想】
-
问题分析:给定 \(N\) 头奶牛和差值 \(K\),一个大小为 \(n\) 的群体若能分裂为两个非空群体且大小相差恰好 \(K\)(即 \(a - b = K\),\(a + b = n\)),则继续分裂;否则停止。求最终停止的群体数量。这是一个递归分治问题,关键在于发现分裂条件可解出 \(a = (n+K)/2\),\(b = (n-K)/2\),要求 \(n > K\) 且 \(n-K\) 为偶数。
-
算法选择:
- 递归模拟:直接按照分裂规则递归计算,每个群体要么分裂为两个继续递归,要么计数为 \(1\)
- 数学判定:分裂条件为 \((n-K) \% 2 = 0\) 且 \(n > K\),此时 \(a = (n+K)/2\),\(b = (n-K)/2\)
-
关键步骤:
- 读取输入:\(N, K\)
- 递归函数
calc(n, k):- 若 \((n-K) \% 2 = 0\) 且 \(n > K\):可分裂
- \(a = (n+K)/2\),\(b = (n-K)/2\)
- 返回
calc(a, k) + calc(b, k)
- 否则:不可分裂,返回 \(1\)
- 若 \((n-K) \% 2 = 0\) 且 \(n > K\):可分裂
- 输出
calc(N, K)
-
时间/空间复杂度:
- 时间复杂度:\(O(\log N)\),每次分裂规模大致减半
- 空间复杂度:\(O(\log N)\),递归栈深度
-
递归分治的核心思想:
- 分裂方程求解:由 \(a + b = n\) 和 \(a - b = K\) 联立得 \(a = (n+K)/2\),\(b = (n-K)/2\)。要求 \(a, b\) 为正整数,即 \(n > K\) 且 \(n-K\) 为偶数
- 二叉树式分裂:每次分裂形成两个子问题,递归求解后累加叶子节点数(不可再分的群体数)
- 终止条件:当群体大小无法表示为两个相差 \(K\) 的正整数之和时,该群体成为叶子节点
- 适用于"规则递归分裂 + 计数叶子节点"类问题,核心是正确表达分裂条件和终止条件
【解题思路】

【算法标签】
入门 #递归
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, k, ans;
int calc(int n, int k) // 定义递归函数
{
int a, b, ans=0; // 定义局部变量a和b,以及群数,初始为0
if ((n-k)%2==0 && n>k) { // 如果b为整数(则a也是整数),且当前这个数n大于k(即可分裂)
a = (n+k)/2; // 计算分裂a、b两组的值
b = (n-k)/2;
ans += calc(a, k); // 使用a和k继续递归查找,从最后一层逐级加上ans的值,即有多少无法分裂的数,就加多少个1
ans += calc(b, k); // 使用b和k继续递归查找
} else { // 无法再按分裂方式分裂
ans = 1; // 则群数为1
}
return ans; // 返回这个群数
}
int main()
{
cin >> n >> k; // 输入n和k
ans = calc(n, k); // 使用递归方式计算
cout << ans << endl; // 打印群数
return 0;
}
【运行结果】
6 2
3
浙公网安备 33010602011771号