bzoj1047理想的正方形
纪念又双叒叕的一道暴力碾标算的题
我们考虑纯暴力
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int a,b,n; int map[1010][1010]; int ans=0x3f3f3f3f; int main() { scanf("%d%d%d",&a,&b,&n); for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) scanf("%d",&map[i][j]); for(int i=1;i+n-1<=a;i++) for(int j=1;j+n-1<=b;j++){ int maxx=0,minn=0x3f3f3f3f; for(int l=i;l<=n+i-1;l++) for(int r=j;r<=n+j-1;r++) maxx=max(maxx,map[l][r]), minn=min(minn,map[l][r]); ans=min(ans,maxx-minn); } printf("%d",ans); return 20020902; }
然后显然,果断$TLE$
那么我们考虑怎么优化暴力
别跟我提什么数据结构啊,单调队列
本小可爱一个也不会
这两道题也是矩阵,然后我们是用的维护二维前缀和来找的在一个矩形内的某些数值
那么,这道题是不是也可以类似的做呢?
由于询问的都是正方形,
我们可以预处理出来
所有正方形的最大最小值
($ps:$从$(1,1)$开始计数)
定义$maxx[i][j][k]$表示以$(i,j)$为左上端点,然后边长为$k$的正方形最大值,$minn[i][j][k]$表示最小。
通过类比上两道题,还有画图,得出
$$maxx[i][j][k]=max(max(maxx[i][j][k-1],maxx[i+1][j+1][k-1]),max(maxx[i][j+1][k-1],maxx[i+1][j][k-1]))$$
$$minn[i][j][k]=min(min(minn[i][j][k-1],minn[i+1][j+1][k-1]),min(minn[i][j+1][k-1],minn[i+1][j][k-1]))$$
所以,我们可以求出来$maxx[][][n],minn[][][n]$
然后枚举左上角端点然后更新答案就行
别以为这样就好了
您写完了之后本地编译了么?
是不是编译未成功?
因为$maxx,minn$这样要开$1001*1001*1001=1e9$辣么大的数组
显然开不下啊$qwq$
那怎么办?
凉拌啊!
发现我们推$maxx,minn$的时候,每次只涉及到$k,k-1$,并且只涉及到$i+1,j+1,i,j$这几个东东,所以可以类似滚动数组优化,滚掉$k$这一维
然后又是省选题
开个$O2$也没什么大不了的
其实是本宝宝懒得写优化了
上代码:
看,连$1kb$都不到
真是暴力碾标算的好题啊$qwq$
// luogu-judger-enable-o2 #pragma GCC optimize (2) #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int a,b,n; int map[1010][1010]; int maxx[1010][1010]; int minn[1010][1010]; int ans=0x3f3f3f3f; int main() { scanf("%d%d%d",&a,&b,&n); for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) scanf("%d",&map[i][j]), maxx[i][j]=minn[i][j]=map[i][j]; for(int k=2;k<=n;k++) for(int i=1;i+k<=a+1;i++) for(int j=1;j+k<=b+1;j++) maxx[i][j]=max(max(maxx[i][j],maxx[i+1][j+1]),max(maxx[i][j+1],maxx[i+1][j])), minn[i][j]=min(min(minn[i][j],minn[i+1][j+1]),min(minn[i][j+1],minn[i+1][j])), ans=k==n?min(ans,maxx[i][j]-minn[i][j]):0x3f3f3f3f; printf("%d",ans); return 0; } /* 5 4 2 1 2 5 6 0 17 16 0 16 17 2 1 2 10 2 1 1 2 2 2 */