Codeforces 335D Rectangles and Square 暴力 + DP

题目大意:

给定n个矩形(1 <= n <= 10^5), 每个矩形的顶点都在整点上, 且矩形两两重叠面积是0(最多只有边重合)

每个矩形给出其左下角坐标和右上角坐标, 坐标(x, y)满足(0 <= x <= 3000, 0 <= y <= 3000)

求是否存在一个正方形,这个正方形刚好是这n个矩形中的某些所拼凑起来的,(拼凑不能有缝隙), 如果有就找出这样的一组矩形, 否则输出NO


大致思路:

这题感觉dp还是有点巧妙的, 再就是巴黎枚举每个矩形的左下角是否是要找的正方形的左下角, 利用预处理好的数组来判断是否能继续增加边长

很巧妙吧还是感觉这种做法, 细节都写在代码注释里了


代码如下:

Result  :  Accepted     Memory  :  179908 KB     Time  :  872 ms

/*
 * Author: Gatevin
 * Created Time:  2015/3/4 14:41:14
 * File Name: Kotori_Itsuka.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 用cov[i][j]表示在所有的格子当中在矩形(1, 1)至矩形(i, j)中被覆盖的正方形的个数
 * covl[i][j]表示第i列(横坐标为i)中从纵坐标1到j中对应的矩形被覆盖的个数且作为某个矩形的左边界
 * covr[i][j]对应右边界, covd[i][j]下边界, covu[i][j]上边界
 * (x1[i], y1[i], x2[i], y2[i])对应矩形i的左下角和右上角
 * 每次枚举矩形n的左下角作为最后形成正方形的左下角, 然后枚举边长len利用cov系列数组判断是否合适
 */
int n;
int cov[3010][3010], covl[3010][3010], covr[3010][3010], covd[3010][3010], covu[3010][3010];
int x1[100010], y1[100010], x2[100010], y2[100010];
vector<int> ans;

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d %d %d %d", x1 + i, y1 + i, x2 + i, y2 + i);
    for(int i = 1; i <= n; i++)
    {
        for(int x = x1[i] + 1; x <= x2[i]; x++)
            for(int y = y1[i] + 1; y <= y2[i]; y++)
                cov[x][y] = 1;
        for(int x = x1[i] + 1; x <= x2[i]; x++)
            covd[x][y1[i] + 1] = covu[x][y2[i]] = 1;
        for(int y = y1[i] + 1; y <= y2[i]; y++)
            covl[x1[i] + 1][y] = covr[x2[i]][y] = 1;
    }
    for(int i = 1; i <= 3000; i++)
        for(int j = 1; j <= 3000; j++)
        {
            cov[i][j] += cov[i - 1][j] + cov[i][j - 1] - cov[i - 1][j - 1];
            covd[i][j] += covd[i - 1][j];
            covu[i][j] += covu[i - 1][j];
            covl[i][j] += covl[i][j - 1];
            covr[i][j] += covr[i][j - 1];
        }
    for(int i = 1; i <= n; i++)//枚举矩形的左下角
    {
        int len = 1, x = x1[i], y = y1[i];//len表示边长
        for(; covl[x + 1][y + len] - covl[x + 1][y] == len && covd[x + len][y + 1] - covd[x][y + 1] == len; len++)
        {
            if(cov[x + len][y + len] - cov[x][y + len] - cov[x + len][y] + cov[x][y] != len*len) break;
            if(covu[x + len][y + len] - covu[x][y + len] == len && covr[x + len][y + len] - covr[x + len][y] == len)
            {
                for(int j = 1; j <= n; j++)//只要左下角在这个范围, 按照cov数组的保证和题意矩形一定都在这个范围
                    if(x1[j] >= x && x1[j] < x + len && y1[j] >= y && y1[j] < y + len)
                        ans.push_back(j);
                printf("YES %d\n", (int)ans.size());
                for(unsigned int j = 0; j < ans.size(); j++)
                    printf("%d ", ans[j]);
                return 0;
            }
        }
    }
    printf("NO\n");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值