2018.10.02 NOIP模擬 矩陣分組(二分答案)
描述
有N行M列的矩陣,每個格子中有一個數字,現在需要你將格子的數字分為A,B兩部分 要求: 1、每個數字恰好屬於兩部分的其中一個部分 2、每個部分內部方塊之間,可以上下左右相互到達,且每個內部方塊之間可以相互到達,且最多拐一次彎 如:
AAAAA AAAAA AAAAA AABAA BaAAA AAABB ABBBA BBAAA AAABB AABAA BaAAA ABBBB AAAAA AAAAA BBBBB
(1) (2) (3) 其中(1)(2)是不允許的分法,(3)是允許的分法。在(2)中,a屬於A區域,這兩個a元素之間互相到達,但是不滿足只拐一次彎到達。 問:對於所有合法的分組中,A區域和B區域的極差,其中極差較大的一個區域最小值是多少 提示:極差就是區域內最大值減去最小值。
輸入
第一行兩個正整數n,m 接下來n 行,每行m個自然數A_{i,j}表示權值
輸出
輸出一行表示答案
樣例輸入
4 4 1 12 6 11 11 4 2 14 10 1 9 20 4 17 13 10
樣例輸出
11
提示
【樣例解釋】 1 12 6 11 11 4 2 14 10 1 9 20 4 17 13 10 分法不唯一,如圖是一種合法的分法。左邊部分極差12-1=11,右邊一塊極差20-10=10,所以答案取這兩個中較大者11。沒有別的分法,可以使答案更小。
測試點 | N,m範圍 |
---|---|
1,2 | n<=10,m<=10 |
3-4 | n=1,m<=2000 |
5-7 | n<=200,m<=200 |
8-10 | n<=2000,m<=2000 |
所有權值1<=a_ij<=10^9
考場上並不會寫二分的check函式,下來看了看題解發現真是妙極。 不難想到每次直接從四個角各按階梯狀拓展出合法區域A,再檢驗B是否合法就行了。(然而考場上寫的棄療了) 於是題解用了一些小技巧優化了一波。 我們在讀入矩陣的時候可以存它在旋轉時的狀態。 然後這四次拓展可以封裝成為同一個函式。 好些的一匹233. 程式碼:
#include<bits/stdc++.h>
#define N 2005
#define inf 2000000000
using namespace std;
int a[4][N][N],n,m;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int X0,X1,X2,X3,Y0,Y1,Y2,Y3,tmp,mx,mn=inf,ed[N],Mx[N][2],Mn[N][2],w[N];
inline bool check(int dir,int tmp){
if(dir)swap(n,m);
ed[0]=m;
for(int i=1,j;i<=n;++i){
for(j=1;j<=ed[i-1];++j)if(mx-a[dir][i][j]>tmp)break;
ed[i]=j-1;
}
for(int i=1;i<=n;++i)for(int j=ed[i]+1;j<=m;++j)if(a[dir][i][j]-mn>tmp){if(dir)swap(n,m);return false;}
if(dir)swap(n,m);
return true;
}
inline bool pd(int tmp){return (check(0,tmp)||check(1,tmp)||check(2,tmp)||check(3,tmp));}
int main(){
n=read(),m=read();
if(n==1){
memset(Mx,-0x3f,sizeof(Mx)),memset(Mn,0x3f,sizeof(Mn));
for(int i=1;i<=m;++i){
w[i]=read();
Mx[i][0]=max(Mx[i-1][0],w[i]);
Mn[i][0]=min(Mn[i-1][0],w[i]);
}
for(int i=m;i;--i){
Mx[i][1]=max(Mx[i+1][1],w[i]);
Mn[i][1]=min(Mn[i+1][1],w[i]);
}
int ans=1e9;
for(int i=1;i<=m;++i)ans=min(ans,max(Mx[i][0]-Mn[i][0],Mx[i+1][1]-Mn[i+1][1]));
printf("%d",ans);
return 0;
}
X0=1,X1=1,X2=n,X3=m,Y0=1,Y1=n,Y2=m,Y3=1,tmp;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
tmp=a[0][X0][Y0++]=a[1][X1++][Y1]=a[2][X2][Y2--]=a[3][X3--][Y3]=read();
if(tmp>mx)mx=tmp;
if(tmp<mn)mn=tmp;
}
++X0,Y0=1,--Y1,X1=1,--X2,Y2=m,++Y3,X3=m;
}
int ans=mx-mn,l=0,r=mx-mn;
while(l<=r){
int mid=l+r>>1;
if(pd(mid))ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
return 0;
}