【2018/10/02測試T2】【WOJ 4019】矩陣分組
【題目】
題目描述:
有 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 區域和 B 區域的極差,其中極差較大的一個區域最小值是多少
提示:極差就是區域內最大值減去最小值。
輸入格式:
第一行兩個正整數 N,M
接下來 N 行,每行 M 個自然數 表示權值
輸出格式:
輸出一行表示答案
樣例資料:
輸入
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,6,7 | n ≤ 200,m ≤ 200 |
8,9,10 | n ≤ 2000,m ≤ 2000 |
所有權值 1 ≤ ≤
【分析】
看到“極差較大的一個區域最小值是多少”這樣的字眼的時候其實就知道這道題多半是二分
但是想到二分之後,check 中間值貌似又無從下手
於是在考場上打了個 20 分暴力就沒去管它了
話不多說,開始講正解,正解做法也就是二分,不過這個 check 比較巧妙
很容易想到的是,對於一個合法的分法,A 和 B 應該是呈階梯狀
設 A 分佈在左上角,將矩陣旋轉三次就可以得到所有的情況,因此寫一個 check 函式就行
設最大值在 A 區域,那麼我們從第一行開始找到第一個與最大值差值不在大於 的位置
在第二行不超過 的位置同樣找一個這樣的邊界, 以下的所有行都這樣弄
這樣子我們就將矩陣劃分為了兩個區域,我們枚舉出來的左邊區域一定滿足條件,那麼我們再判斷右區域滿不滿足(與最小值的差值在條件範圍內,也即都小於等於 )
把旋轉的四種情況都判斷一下就可以得出答案
【程式碼】
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
int Read()
{
int x=0;
char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c))
{
x=(x<<1)+(x<<3)+(c^'0');
c=getchar();
}
return x;
}
int n,m,maxn,minn;
int rec[N],a[10][N][N];
bool Check(int id,int mid)
{
int i,j;
if(!(id&1))
swap(n,m);
rec[0]=m;
for(i=1;i<=n;++i)
{
for(j=1;j<=rec[i-1];++j)
if(maxn-a[id][i][j]>mid)
break;
rec[i]=j-1;
}
for(i=1;i<=n;++i)
for(j=rec[i]+1;j<=m;++j)
if(a[id][i][j]-minn>mid)
{
if(!(id&1))
swap(n,m);
return false;
}
if(!(id&1))
swap(n,m);
return true;
}
bool check(int mid)
{
if(Check(1,mid)) return true;
if(Check(2,mid)) return true;
if(Check(3,mid)) return true;
if(Check(4,mid)) return true;
return false;
}
int main()
{
// freopen("matrix.in","r",stdin);
// freopen("matrix.out","w",stdout);
int i,j,k;
maxn=0,minn=1e+9;
n=Read(),m=Read();
int x1=1,y1=1,x2=1,y2=n,x3=n,y3=m,x4=m,y4=1;
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j)
{
k=Read();
maxn=max(maxn,k);
minn=min(minn,k);
a[1][x1][y1++]=k;
a[2][x2++][y2]=k;
a[3][x3][y3--]=k;
a[4][x4--][y4]=k;
}
x1++;y1=1;
y2--;x2=1;
x3--;y3=m;
y4++;x4=m;
}
int l,r,mid;
l=0,r=maxn-minn;
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d",l);
// fclose(stdin);
// fclose(stdout);
return 0;
}