题解:洛谷 P9944 Comfortable Cows
【题目来源】
洛谷:P9944 [USACO21FEB] Comfortable Cows B - 洛谷
【题目描述】
Farmer John 的草地可以被看作是一个由正方形方格组成的巨大的二维方阵(想象一个巨大的棋盘)。初始时,草地上是空的。
Farmer John 将会逐一地将 \(N\)(\(1\le N\le 10^5\))头奶牛加入到草地上。第 \(i\) 头奶牛将会占据方格 \((x_i,y_i)\),不同于所有已经被其他奶牛占据的方格(\(0\le x_i,y_i\le 1000\))。
一头奶牛被称为是「舒适的」,如果它水平或竖直方向上与恰好三头其他奶牛相邻。Farmer John 对他的农场上舒适的奶牛数量感兴趣。对 \(1\ldots N\) 中的每一个 \(i\),输出第 \(i\) 头奶牛加入到草地上之后舒适的奶牛的数量。
【输入】
输入的第一行包含一个整数 \(N\)。以下 \(N\) 行每行包含两个空格分隔的整数,表示一头奶牛所在的方格坐标 \((x,y)\)。输入保证所有方格的坐标是不同的。
【输出】
输出的第 \(i\) 行包含前 \(i\) 头奶牛加入到草地上之后舒适的奶牛的数量。
【输入样例】
8
0 1
1 0
1 1
1 2
2 1
2 2
3 1
3 2
【输出样例】
0
0
0
1
0
0
1
2
【核心思想】
-
问题分析:在一个无限大的网格上,逐次添加奶牛(坐标不重复)。每次添加后,需要统计当前所有奶牛中,恰好有3个四连通邻居的奶牛数量。每次只新增一头奶牛,只会影响该奶牛自身及其上下左右四个邻居的“舒适状态”。因此无需每次全局扫描,只需增量更新受影响的5个位置,维护全局舒适奶牛总数。
-
算法选择:
- 增量模拟:用一个二维数组
a标记奶牛位置(坐标范围 \(0 \le x,y \le 1000\),但可适当扩展),用全局变量res记录当前舒适奶牛总数。 - 局部更新:每添加一头新奶牛
(x, y)时,只检查该点及其上下左右四个邻居(最多5个位置)在添加前后的舒适状态变化,将变化量累加到res中。 - 辅助函数:定义一个函数
isComfortable(x, y),返回坐标(x, y)处的奶牛是否舒适(计算其四邻居中奶牛的数量是否等于3)。这样可以避免重复代码。
- 增量模拟:用一个二维数组
-
关键步骤:
- 初始化:读入 \(N\),清空标记数组
a,res = 0。 - 处理每头新奶牛(坐标
(x, y)):- 添加前的检查:遍历受影响位置集合
P = {(x,y), (x-1,y), (x+1,y), (x,y-1), (x,y+1)},对其中每个有奶牛的位置p,若isComfortable(p)为真,则res--(因为这些位置在添加新牛后可能不再舒适)。 - 添加新牛:
a[x][y] = 1。 - 添加后的检查:再次遍历同样的集合
P,对其中每个有奶牛的位置p,若isComfortable(p)为真,则res++。 - 输出当前
res。
- 添加前的检查:遍历受影响位置集合
isComfortable(x, y)函数:检查四个方向(x+dx[k], y+dy[k])是否在网格内且a为1,统计数量,返回是否等于3。
- 初始化:读入 \(N\),清空标记数组
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),每头奶牛只检查常数个(≤5)位置,每个位置检查4个邻居,\(N \le 10^5\),完全可行。
- 空间复杂度:\(O(C^2)\),其中 \(C\) 为坐标范围上限(约1005×1005),也可用哈希表存储奶牛集合以优化空间,但二维数组足够。
-
增量更新 + 局部影响分析的核心思想:
- 局部性:新增一头奶牛只会改变其自身及其四邻居的邻居数量,进而只可能改变这5个位置的舒适状态。利用这一性质,将全局问题转化为常数规模的局部更新。
- 状态修正:先减去受影响的旧状态,再添加新奶牛,最后加上受影响的新状态,等价于只计算净变化量,避免了每次全局重算。
- 边界处理:坐标范围可能为0,检查邻居时需确保不越界。
- 适用场景:适用于动态添加元素、且状态仅依赖局部邻居关系的网格模拟问题。核心是通过维护受影响的小范围集合实现 \(O(1)\) 更新。
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, res=0, cnt=0;
int a[1005][1005];
int dx[4] = {-1,0,1,0}, dy[4] = {0,1,0,-1};
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // 遍历n头奶牛
int x, y; // 定义每头奶牛的坐标x和y
int cnt1=0, cnt2=0; // 定义添加前和添加后舒适量的统计
cin >> x >> y; // 输入奶头的坐标
for (int i=x-1; i<=x+1; i++) { // 遍历这台奶牛周围,计算添加之前的舒适量
for (int j=y-1; j<=y+1; j++) {
int ans=0; // 定义周围奶牛的数量,初始为0
if (a[i][j]==1) { // 如果该位置有奶牛
for (int k=0; k<4; k++) { // 则判断周围4个方向是否有奶牛
int xx = i + dx[k];
int yy = j + dy[k];
if (xx<0 || yy<0) continue;
if (a[xx][yy]==1) ans++; // 有的话,ans自增
}
if (ans==3) cnt1++; // 如果ans为3,则舒适的奶牛数量加1
}
}
}
a[x][y] = 1; // 添加输入的奶牛
for (int i=x-1; i<=x+1; i++) { // 再次遍历这台奶牛周围,计算添加之后的舒适量
for (int j=y-1; j<=y+1; j++) {
int ans=0;
if (a[i][j]==1) {
for (int k=0; k<4; k++) {
int xx = i + dx[k];
int yy = j + dy[k];
if (xx<0 || yy<0) continue;
if (a[xx][yy]==1) ans++;
}
if (ans==3) cnt2++; // 如果ans为3,则舒适的奶牛数量加1
}
}
}
res += cnt2-cnt1; // 计算前后的变化,并累加到ans中(有时相减会变为负数)
cout << res << endl; // 最后打印结果
}
return 0;
}
【运行结果】
8
0 1
0
1 0
0
1 1
0
1 2
1
2 1
0
2 2
0
3 1
1
3 2
2
浙公网安备 33010602011771号