山东省第六届ACM省赛 D Stars

针对给定的星星坐标,寻找能够覆盖至少K颗星星的最小面积矩形,边需平行于坐标轴。通过构建01矩阵并应用前缀和、二分查找或尺取法优化搜索过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Stars

Time Limit: 15000 ms  Memory Limit: 65536 KiB
Problem Description

There are N (1 ≤ N ≤ 400) stars in the sky. And each of them has a unique coordinate (x, y) (1 ≤ x, y ≤ N). Please calculate the minimum area of the rectangle (the edges of the rectangle must be parallel to the X, Y axes) that can cover at least K (1 ≤ K ≤ N) stars. The stars on the borders of the rectangle should not be counted, and the length of each rectangle’s edge should be an integer.

Input

Input may contain several test cases. The first line is a positive integer T (T ≤ 10), indicating the number of test cases below.

For each test cases, the first line contains two integers N, K, indicating the total number of the stars and the number of stars the rectangle should cover at least.

Each of the following N lines contains two integers x, y, indicating the coordinate of the stars.

Output

For each test case, output the answer on a single line.

Sample Input
2
1 1
1 1
2 2
1 1
1 2
Sample Output
1
2
Hint
Source

“浪潮杯”山东省第六届ACM大学生程序设计竞赛

题意:

在二维平面上有n个点,从中选出一个一个长方形,要求其中包括至少k个点,且面积最小。

题解:

由于平面中的点所在的位置在[n,n]之间。n最大为400。我们将平面看成一个01矩阵,并做一个前缀和。

枚举矩阵的上下界。然后枚举上界的左端点,二分寻找下界的右端点,使得子矩阵[(i,l) (j,r)] 满足其中的点的个数至少为k

需要注意的是,枚举子矩阵的方法和计算子矩阵中的点的个数

时间复杂度O(n^3*log(n))

#include<bits/stdc++.h>
using namespace std;
const int maxn = 450;
int maze[maxn][maxn];
int n,k;
bool check(int x,int y,int l,int r) {
    int temp = maze[y][r] - maze[y][l-1] - maze[x-1][r] + maze[x-1][l-1];
    return temp >= k;
}
int lowerbound(int x,int y,int sta) {
    int l = sta,r = n;
    while(l <= r)
    {
        int mid = (l+r)>>1;
        if(check(x,y,sta,mid)) r = mid - 1;
        else l = mid + 1;
    }
    if(l == n+1) return -1;
    return l;
}
int main()
{
    int caset;scanf("%d",&caset);
    while(caset--)
    {
        memset(maze,0,sizeof(maze));
        scanf("%d%d",&n,&k);
        for(int i=0,u,v;i<n;i++) {
            scanf("%d%d",&u,&v);
            maze[u][v] = 1;
        }

        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) maze[i][j] += maze[i][j-1];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) maze[i][j] += maze[i-1][j];

        int ans = n * n;
        for(int i=1;i<=n;i++) {
            for(int j=i;j<=n;j++) { /// 枚举矩阵的上下界
                for(int l=1;l<=n;l++) { /// 枚举上界的左端点。
                    int r = lowerbound(i,j,l);
                    if(r != -1) {
                        ans = min(ans,(j-i+1)*(r-l+1));
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


思路二:

由于对于某些二分可以用尺取法进行优化,于是本题也可以使用尺取。

时间复杂度O(n^3)

/**

    给出平面中 n 个整数点,求最小的至少可以覆盖 k 个点的矩形面积
    把这个平面看作一个矩阵,有整数点的位置为 1 ,其他位置为 0 。
    求出 (1,1) 到任意点 (n,m) 的矩阵和,然后枚举任意两行,
    我们可以根据这两行中值的差求得当前判断的子矩阵的和,这里可以用尺取法,若满足条件,计算面积,取最小值。

    将 平面问题转化为 矩阵中子矩阵问题
    利用二维尺取法进行尺取。
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500;
const int INF = 0x3f3f3f3f;
int maze[maxn][maxn];
int n,k;
int cal(int x,int y) {
    int l = 1,r = 1;
    int res = INF;
    /// temp = 子矩阵[(x,l),(y,r)]中的点的个数
    int temp = maze[y][r] - maze[y][l-1] - maze[x-1][r] + maze[x-1][l-1];
    while(1) {
        while(r < n && temp < k) {
            r++;
            temp = maze[y][r] - maze[y][l-1] - maze[x-1][r] + maze[x-1][l-1];
        }
        if(temp < k) break;
        res = min(res,(y-x+1)*(r-l+1));
        if(l < n) {
            l++;
            temp = maze[y][r] - maze[y][l-1] - maze[x-1][r] + maze[x-1][l-1];
        }
        else break;
    }
    return res;
}
int main()
{
    int caset;scanf("%d",&caset);
    while(caset--) {

        memset(maze,0,sizeof(maze));
        scanf("%d%d",&n,&k);
        for(int i=0,u,v;i<n;i++) {
            scanf("%d%d",&u,&v);
            maze[u][v] = 1;
        }
        /// 预处理出 (1,1)到 (n,m) 的 子矩阵中的 点的个数
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                maze[i][j] += maze[i][j-1];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                maze[i][j] += maze[i-1][j];

        int ans = n * n;
        for(int i=1;i<=n;i++) { /// 枚举任意两行,其中包括单独的一行。枚举矩阵的上下界
            for(int j=i;j<=n;j++) {
                ans = min(ans,cal(i,j));
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值