NOIP模擬 矩陣分組(二分答案)
阿新 • • 發佈:2018-12-13
【題目描述】
有N行M列的矩陣,每個格子中有一個數字,現在需要你將格子的數字分為A,B兩部分
要求:
1、每個數字恰好屬於兩部分的其中一個部分
2、每個部分內部方塊之間,可以上下左右相互到達,且每個內部方塊之間可以相互到達,且最多拐一次彎
如:
其中(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函式,20分滾粗。。。
首先推個結論:所選區域構成圖形一定是階梯狀,否則就會出現拐兩次彎或者不相通的情況。
對於每個二分的答案,因為矩陣可以旋轉,所以需檢查四次,對於每次檢查,可以一列一列的掃,每次記錄當前行極值與二分的值比較,如果超過就斷開,記錄每一列的個數看是否遞增或者遞減(二者取一,因為會旋轉)。
【程式碼~】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=2001;
const LL INF=0x3f3f3f3f;
LL n,m;
LL ma[4][MAXN][MAXN];
LL gmax=-INF,gmin=INF;
LL endi[MAXN];
bool pd(LL x,LL u)
{
if(u&1)
swap(n,m);
endi[0]=m;
LL j;
for(LL i=1;i<=n;++i)
{
for(j=1;j<=endi[i-1];++j)
{
if(gmax-ma[u][i][j]>x)
break;
}
endi[i]=j-1;
}
for(LL i=1;i<=n;++i)
{
for(LL j=endi[i]+1;j<=m;++j)
{
if(ma[u][i][j]-gmin>x)
{
if(u&1)
swap(n,m);
return false;
}
}
}
if(u&1)
swap(n,m);
return true;
}
bool check(LL x)
{
if(pd(x,0))
return true;
if(pd(x,1))
return true;
if(pd(x,2))
return true;
if(pd(x,3))
return true;
return false;
}
LL Read()
{
LL i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int main()
{
n=Read(),m=Read();
LL x=1,x1=1,x2=n,x3=m,y=1,y1=n,y2=m,y3=1,t;
for(LL i=1;i<=n;++i)
{
for(LL j=1;j<=m;++j)
{
t=ma[0][x][y++]=ma[1][x1++][y1]=ma[2][x2][y2--]=ma[3][x3--][y3]=Read();
if(t>gmax)
gmax=t;
if(t<gmin)
gmin=t;
}
x++,y=1;
y1--,x1=1;
x2--,y2=m;
y3++,x3=m;
}
LL l=0,r=gmax-gmin;
while(l<r)
{
LL mid=l+r>>1;
if(check(mid))
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}