1. 程式人生 > >JZOJ 5938. 【NOIP2018模擬10.30】分離計劃

JZOJ 5938. 【NOIP2018模擬10.30】分離計劃

題目

一個 n m n*m 的矩陣,分為A和B兩個部分,其中AB兩個部分都要連通,且不能凹。
如何分AB部分,才能使得最後A,B部分的極差最大值最小?
輸出這個最小值。

題解

原題出處:JOI2016 The Kingdom of JOIOI
抓住題目的關鍵點。假設最大值在A塊,最小值在B塊。
如果二分出答案mid之後,則A塊的值域為 [

m a x m i d , m a x
] [max-mid,max] ,B塊的值域為 [ m i n , m
i n + m i d ] [min,min+mid]

又AB兩塊都不凹,所以,分割線必然是從右上到坐下。分界線不會遞增。
比賽的時候被一個地方卡住了。
究竟哪一塊是A塊,哪一塊是B塊。
強制讓左上角是A塊,右下角是B塊。然後變換一下圖形。
旋轉方法:(順時針旋轉90度)

for i=1 to n do
    for j=1 to m do
        b[j][n-i+1]=a[i][j];
memcpy(b,a,sizeof(b));

當然,這題不需要旋轉,只需要翻轉即可。
當然還有更優的 O ( n m ) O(nm) 方法。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 2010
#define P(a) putchar(a)
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int i,j,k,l,n,m,ans;
int a[N][N],b[N][N];
int wl,wr,wm,vw,vx,MX,MN;
int vl,vr,vm;
bool i1,i2;
int read(){
	int fh=0,rs=0;char ch=0;
	while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
	if(ch=='-')fh=1,ch=getchar();
	while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
	return fh?-rs:rs;
}
void write(int x){
	if(x>9)write(x/10);
	P(x%10+'0');
}
bool check(int vm){
	int i,j=m,k,l;
	fo(i,1,n){
		l=j;
		fo(k,1,j){
			if(MX-a[i][k]>vm){
				l=k-1;
				break;
			}
		}
		fo(k,l+1,m)
		    if(a[i][k]-MN>vm){
		    	return 0;
			}
		j=l;
	}
	return 1;
}
void doit(){
	vl=0,vr=MX-MN;vx=vl;
	while(vl<=vr){
		vm=(vl+vr)>>1;
		if(check(vm))vx=vm,vr=vm-1;else vl=vm+1;
	}
	ans=Min(ans,vx);
}
void fanzhuan0(){
	int i,j;
	fo(i,1,n)fo(j,1,m/2)swap(a[i][j],a[i][m-j+1]);
}
void fanzhuan1(){
	int i,j;
	fo(i,1,n/2)fo(j,1,m)swap(a[i][j],a[n-i+1][j]);
}
int main(){
	n=read();m=read();
	MX=0,MN=2147483647;
	fo(i,1,n)fo(j,1,m){
		a[i][j]=read();
		MX=Max(MX,a[i][j]);
		MN=Min(MN,a[i][j]);
	}
	ans=2147483647;
	doit();fanzhuan0();
	doit();fanzhuan1();
	doit();fanzhuan0();
	doit();
	printf("%d",ans);
	return 0;
}~