1. 程式人生 > >2018.10.02 NOIP模擬 矩陣分組(二分答案)

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是否合法就行了。(然而考場上寫的棄療了) 於是題解用了一些小技巧優化了一波。 我們在讀入矩陣的時候可以存它在旋轉0901802700度,90度,180度,270度時的狀態。 然後這四次拓展可以封裝成為同一個函式。 好些的一匹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; }