ICPC2018 Nakhon Pathom- Largest Allowed Area(最大允许区域) (二分查询+二维前缀和)

A company is looking for land to build its headquarters. It has a lot of money and can buy as many land patches as it needs. Its goal, however, is finding the largest square region containing no forest. Unfortunately, there is no such region that is large enough for the headquarters they want to build. 

一家公司正在找一块空地去建它的总部。  这家公司资金充裕,所以它可以买尽可能多的土地满足需求。然而它的目标是找到最大的一块正方形土地且没有植被覆盖。不幸的是,这里没有这样的足够大的土地供他们建造总部。
   
After negotiation with the government and the evaluation of environmental impacts, the government allows the company to purchase land with at most one forest patch. In other words, the company’s goal is now finding the largest square region containing at most one forest patch.


在与政府协商以及对环境影响的评估后,政府允诺公司去买一块带有至多一片森林的土地。换句话说,这家公司的目标是找到最大的正方形区域,包含至多一片林地。

 
To facilitate the search process, the company creates a map in the form of a 2D table consisting R rows and C columns. In this 2D table, each entry represents a land of patch where 0 corresponds to a non-forest patch and 1 to a forest patch. Unfortunately, the map may have up to 1,000 x 1,000 entries and it is not a good idea to manually look for the largest allowed square region. This is where your skill comes into play. Write an efficient algorithm to find such a square region. 

为了加快搜寻的过程,公司做了一份R×C的2D地图,在这份2D地图里,每一个元素都代表着一片土地:0是空地,1是林地。不幸的是,这张地图可能有1000×1000那么大,所以人工查找这么一块正方形土地不是明智之举。这也是展现你的技术的时刻了。写一个高效算法去找到这块正方形区域。

输入

The first line is a positive integer T <= 20 representing the number of test cases. For each case, the input is formatted as follows. 


Note: there is at least one non-forest patch in each test case. 

 

输出

There are T lines in the output. Each line is the number of rows in the largest allowed square region for each case. 

 

输入

2 
10 20 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1  
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
20 10 
1 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0
0 0 1 0 0 0 0 1 1 0
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0

                 输出

9
7

因为这个题的本质需求是要找到尽可能大的一片区域,使得区域内部元素和≤1,所以我们使用二维前缀和来解决它。 

根据二维前缀和递推公式:S[x][y]=S[x-1][y]+S[x][y-1]-S[x-1][y-1]+current来建立前缀和表格,其中x,y为当前所示矩形范围的右下顶点,current为当前输入的元素值,公式证明在此不作赘述。

已经建立了表格,那么要怎么查询它呢?根据公式Query=S[x][y]-S[x-1][y]-S[x][y-1]+S[x-1][y-1],其中Query为所求值。(其中的1可以换成任意常数,这代表所查询矩阵的长与宽。特别的,当两个数相等时,查询的是一个方阵。)

由于题目本身数据相对较大,且本方法下查询时必须要有两个for循环来遍历每个点作右下顶点,所以对确定顶点的矩阵,我们使用二分查找来对边长进行搜索。

于是有这样的思路:

  1. 建立二维前缀和表
  2. 对每一个点进行遍历,并用二分算法解决边长问题,并记录最大值。

数据较大,如果使用scanf依然超时,那么可选择使用代码中提供的快速读入

代码如下: 

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline ll lldcin()
{
    ll tmp=0,si=1;char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')
            si=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
ll sum[1050][1050];
int DETERMINATION()
{
    ll t;
    t=lldcin();
    while(t--)
    {
        reset(sum,0);
        ll r,c;
        r=lldcin(),c=lldcin();
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
        {
            ll tmp;
            tmp=lldcin();
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+tmp;//二维前缀和建立
        }
//        cout<<endl;
//        for(int i=1;i<=r;i++)
//        {
//            for(int j=1;j<=c;j++)
//                cout<<sum[i][j]<<" ";
//          cout<<endl;
//        }
        ll ans=-1;
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
        {
            ll left=1,right=min(i,j);
            while(left<=right)
            {
                ll mid=(left+right)>>1;
                if(sum[i][j]+sum[i-mid][j-mid]-sum[i-mid][j]-sum[i][j-mid]<=1)//二维前缀和查询
                {
                  ans=max(ans,mid);
                  left=mid+1;
                }
                else
                    right=mid-1;
            }
        }
        printlnlld(ans);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值