[USACO07FEB]新牛棚Building A New Barn

洛谷题目链接:[USACO07FEB]新牛棚Building A New Barn

题目描述

After scrimping and saving for years, Farmer John has decided to build a new barn. He wants the barn to be highly accessible, and he knows the coordinates of the grazing spots of all N (2 ≤ N ≤ 10,000 cows. Each grazing spot is at a point with integer coordinates (Xi, Yi) (-10,000 ≤ Xi ≤ 10,000; -10,000 ≤ Yi ≤ 10,000). The hungry cows never graze in spots that are horizontally or vertically adjacent.

The barn must be placed at integer coordinates and cannot be on any cow's grazing spot. The inconvenience of the barn for any cow is given the Manhattan distance formula | X - Xi | + | Y - Yi|, where (X, Y) and (Xi, Yi) are the coordinates of the barn and the cow's grazing spot, respectively. Where should the barn be constructed in order to minimize the sum of its inconvenience for all the cows?
给出平面上n个不相邻的点,要求到这n个点的曼哈顿距离之和最小的点的个数ans2,和这个最小距离ans1。

输入输出格式

输入格式:

Line 1: A single integer: N

Lines 2..N+1: Line i+1 contains two space-separated integers which are the grazing location (Xi, Yi) of cow i

输出格式:

Line 1: Two space-separated integers: the minimum inconvenience for the barn and the number of spots on which Farmer John can build the barn to achieve this minimum.

输入输出样例

输入样例#1:

4
1 -3
0 1
-2 1
1 -1

输出样例#1:

10 4

说明

The minimum inconvenience is 10, and there are 4 spots that Farmer John can build the farm to achieve this: (0, -1), (0, 0), (1, 0), and (1, 1).

简述一下题意:给出一个二维平面上\(n\)个点.要求出\(ans2\)个点使得这\(ans2\)个点到所有点的曼哈顿距离之和,这\(ans2\)个点不能是原平面直角坐标系中给出的点.两点间曼哈顿距离的公式为\(\left|x-x_i\right|+\left|y-y_i\right|\).

既然要到所有点的曼哈顿距离之和最小,那么可以先设答案点坐标为\((x,y)\).可以得到这样一个式子:

\[ans1=\sum{(\left|x-x_i\right|+\left|y-y_i\right|)}\]

显然我们是要求出一个\((x,y)\)使得\(ans1\)最小,因为\(x\),\(y\)互不影响,所以可以分开处理,那么根据我们的数学知识,可以得知使\(ans1\)最小的值就是\(x\)序列的中位数,同理\(y\)也是序列中的中位数.

这样我们就求出了\(ans1\),但是因为题目的限制,如果\(n\)为奇数时,\(x\)\(y\)直接求出的中位数有可能是原图中给出的点.所以这时我们要对这个点的上下左右进行判断,对上下左右求一遍最小值.

如果\(n\)为偶数时,那么在\(x[n/2],x[n/2+1],y[n/2],y[n/2+1]\)这四个点所围成的矩形中的所有点都是满足条件的. 先计算出这个矩形中包含的点的个数,然后再将原图中包含的点都一个个删掉.

#include<bits/stdc++.h>
using namespace std;
const int inf=2147483647;
const int N=10000+5;

int n, x[N], y[N], ans1 = inf, ans2 = 0;
int dir[]={0,1,0,-1,0};

struct node{
    int x, y;
}p[N];

int gi(){
    int ans = 0 , f = 1; char i = getchar();
    while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
    while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
    return ans * f;
}

int main(){
    cin >> n;
    for(int i=1;i<=n;i++) x[i] = gi(), y[i] = gi();
    for(int i=1;i<=n;i++) p[i].x = x[i], p[i].y = y[i];
    sort(x+1 , x+n+1); sort(y+1 , y+n+1);
    if(n & 1){
        int a = x[n/2+1], b = y[n/2+1], sum = 0;
        for(int i=1;i<=n;i++)
            sum += abs(a-x[i])+abs(b-y[i]);
        ans1 = sum; ans2 = 1;
        for(int i=1;i<=n;i++)
            if(p[i].x == a && p[i].y == b) ans1 = inf;
        for(int i=0;i<4;i++){
            int nx = a+dir[i], ny = b+dir[i+1], sum = 0;
            for(int i=1;i<=n;i++)
            sum += abs(nx-x[i])+abs(ny-y[i]);
            if(sum < ans1) ans1 = sum, ans2 = 1;
            else if(sum == ans1) ans2++;
        }
    }
    else{
        int x1 = x[n/2], x2 = x[n/2+1];
        int y1 = y[n/2], y2 = y[n/2+1], sum = 0;
        for(int i=1;i<=n;i++)
            sum += abs(x1-x[i])+abs(y1-y[i]);
        ans1 = min(ans1 , sum);
        ans2 = (x2-x1+1)*(y2-y1+1);
        for(int i=1;i<=n;i++)
            if(x1<=p[i].x && p[i].x<=x2 && y1<=p[i].y && p[i].y<=y2) ans2--;
    }
    printf("%d %d\n",ans1,ans2);
    return 0;
}

转载于:https://www.cnblogs.com/BCOI/p/8870220.html

### 解题思路 此问题的核心在于通过 **二维差分** 和 **前缀和** 的方法来高效计算被指定层数 $ K $ 涂漆覆盖的区域大小。以下是详细的分析: #### 1. 题目背景 农夫约翰希望在他的谷仓上涂油漆,目标是找到最终被恰好 $ K $ 层油漆覆盖的总面积。给定若干矩形区域及其对应的涂漆操作,我们需要统计这些操作完成后满足条件的区域。 #### 2. 差分法的应用 为了快速更多个连续单元格的状态并查询其总和,可以采用 **二维差分** 技术。具体来说: - 初始化一个二维数组 `diff` 来表示差分矩阵。 - 对于每一个矩形 $(x_1, y_1)$ 到 $(x_2, y_2)$,我们可以通过如下方式更差分矩阵: ```python diff[x1][y1] += 1 diff[x1][y2 + 1] -= 1 diff[x2 + 1][y1] -= 1 diff[x2 + 1][y2 + 1] += 1 ``` 上述操作的时间复杂度仅为常数级别 $ O(1) $,因此非常适合大规模数据集的操作[^1]。 #### 3. 前缀和恢复原矩阵 完成所有矩形的差分更后,利用前缀和算法还原实际的涂漆次数矩阵 `paints`。对于每个位置 $(i,j)$,执行以下操作: ```python for i in range(1, n + 1): for j in range(1, m + 1): paints[i][j] = (paints[i - 1][j] + paints[i][j - 1] - paints[i - 1][j - 1] + diff[i][j]) ``` 这里需要注意边界条件以及初始值设置为零的情况[^4]。 #### 4. 统计符合条件的区域 最后遍历整个 `paints` 数组,累加那些等于 $ K $ 的元素数量即可得到答案。 --- ### 实现代码 下面是基于以上理论的一个 Python 实现版本: ```python def painting_the_barn(): import sys input_data = sys.stdin.read().splitlines() N, K = map(int, input_data[0].split()) max_x, max_y = 0, 0 rectangles = [] for line in input_data[1:]: x1, y1, x2, y2 = map(int, line.split()) rectangles.append((x1, y1, x2, y2)) max_x = max(max_x, x2) max_y = max(max_y, y2) # Initialize difference array with extra padding to avoid boundary checks. size = max(max_x, max_y) + 2 diff = [[0]*size for _ in range(size)] # Apply all rectangle updates using the difference method. for rect in rectangles: x1, y1, x2, y2 = rect diff[x1][y1] += 1 diff[x1][y2 + 1] -= 1 diff[x2 + 1][y1] -= 1 diff[x2 + 1][y2 + 1] += 1 # Compute prefix sums from differences to get actual paint counts. paints = [[0]*size for _ in range(size)] result = 0 for i in range(1, size): for j in range(1, size): paints[i][j] = ( diff[i][j] + paints[i - 1][j] + paints[i][j - 1] - paints[i - 1][j - 1] ) if paints[i][j] == K: result += 1 return result print(painting_the_barn()) # Output final answer as per sample output format. ``` --- ### 结果验证 按照样例输入测试该程序能够正确返回预期的结果即8单位面积被两层涂料所覆盖[^2]。 --- ### 性能优化建议 如果进一步追求效率还可以考虑压缩坐标范围减少内存消耗或者使用更底层的语言实现核心逻辑部分比如 C++ 或 Java 等[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值