JOIOI王國 - 二分+貪心

題面

題解

通過一句經典的話“最大值的最小值” 我判斷它是二分題,

不難發現,整個圖形中兩個省的分界線是一條單調不遞減或單調不遞增的折線。

而且,越到後來它的最大值只會越來越大,最小值只會越來越小,極差只會越來越大。

所以如果我們把ans上界定下來了,我們就可以貪心的讓它其中一個區域的值在 [maxa - ans , maxa] 的灰色地帶發展,然後判斷另一個區域是否合法。由於另一個區域的點數越少越好,所以在灰色地帶要儘量發展得廣。

我們只能先定義一個 p 表示當前行兩省的分界, 然後在第一行儘量擴展得遠,下一行在 [0 , p]之間擴展得遠,這樣不僅可以讓另一個區域的點更少,而且可以讓下一行有更大的發展空間。

其它的貪心方法就不行了,所以分別行數從小到大和從大到小各枚舉一次,然後做個左右對稱再來。

CODE

#include<cstdio> 
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#define LL long long
using namespace std;
inline LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
	while(s >= '0' && s <= '9'){x = x * 10 + s - '0';s = getchar();}
	return x * f;
}
LL n,m,i,j,s,o,k,flag = 0;
LL a[2005][2005];
int b[2005],c[2005];
int x1 = 1,x2 = 1,y1 = 1,y2 = 1;
LL maxl[2005][2005];
LL maxr[2005][2005];
LL minl[2005][2005];
LL minr[2005][2005];
LL ans = 1e18;
bool check(LL mid) {
	LL minn = a[x1][y1] - mid;
//	cout<<minn<<endl;
	LL p = m,mi1 = 0x7f7f7f7f,mi2 = 0x7f7f7f7f,ma1=0,ma2=0;
	for(int i = 1;i <= n;i ++) {
		int pp = p;
		for(int j = 0;j <= pp;j ++) {
			if(minl[i][j] >= minn) {
				p = j;
			}
		}
		mi1 = min(mi1,minl[i][p]);
		ma1 = max(ma1,maxl[i][p]);
		mi2 = min(mi2,minr[i][p + 1]);
		ma2 = max(ma2,maxr[i][p + 1]);
	}
//	cout<<"ok1"<<endl;
	if(max(ma1 - mi1,ma2 - mi2) <= mid) return 1;
	p = m,mi1 = 0x7f7f7f7f,mi2 = 0x7f7f7f7f,ma1=0,ma2=0;
	for(int i = n;i > 0;i --) {
		int pp = p;
		for(int j = 0;j <= pp;j ++) {
			if(minl[i][j] >= minn) {
				p = j;
			}
		}
		mi1 = min(mi1,minl[i][p]);
		ma1 = max(ma1,maxl[i][p]);
		mi2 = min(mi2,minr[i][p + 1]);
		ma2 = max(ma2,maxr[i][p + 1]);
	}
//	cout<<"ok2"<<endl;
	if(max(ma1 - mi1,ma2 - mi2) <= mid) return 1;
	p = 0,mi1 = 0x7f7f7f7f,mi2 = 0x7f7f7f7f,ma1=0,ma2=0;
	for(int i = 1;i <= n;i ++) {
		int pp = p;
		for(int j = m;j >= pp;j --) {
			if(minr[i][j + 1] >= minn) {
				p = j;
			}
		}
		mi1 = min(mi1,minl[i][p]);
		ma1 = max(ma1,maxl[i][p]);
		mi2 = min(mi2,minr[i][p + 1]);
		ma2 = max(ma2,maxr[i][p + 1]);
	}
//	cout<<"ok3"<<endl;
	if(max(ma1 - mi1,ma2 - mi2) <= mid) return 1;
	p = 0,mi1 = 0x7f7f7f7f,mi2 = 0x7f7f7f7f,ma1=0,ma2=0;
	for(int i = n;i > 0;i --) {
		int pp = p;
		for(int j = m;j >= pp;j --) {
			if(minr[i][j + 1] >= minn) {
				p = j;
			}
		}
//		printf("p---%d\n",p);
		mi1 = min(mi1,minl[i][p]);
		ma1 = max(ma1,maxl[i][p]);
		mi2 = min(mi2,minr[i][p + 1]);
		ma2 = max(ma2,maxr[i][p + 1]);
	}
//	cout<<"ok4"<<endl;
	if(max(ma1 - mi1,ma2 - mi2) <= mid) return 1;
	return 0;
}
LL solve(LL l,LL r) {
//	printf("%lld %lld\n",l,r);
	if(l >= r - 1) {
		if(check(l)) return l;
		return r;
	}
	LL mid = (l + r) / 2;
	if(check(mid)) return solve(l,mid);
	return solve(mid,r);
}
int main() {
	n = read();m = read();
	for(int i = 1;i <= n;i ++) {
		minl[i][0] = 1e18;
		for(int j = 1;j <= m;j ++) {
			a[i][j] = read();
			if(a[i][j] > a[x1][y1]) x1 = i,y1 = j;
			if(a[i][j] < a[x2][y2]) x2 = i,y2 = j;
			minl[i][j] = min(minl[i][j - 1],a[i][j]);
			maxl[i][j] = max(maxl[i][j - 1],a[i][j]);
		}
		minr[i][m + 1] = 1e18;
		for(int j = m;j > 0;j --) {
			minr[i][j] = min(minr[i][j + 1],a[i][j]);
			maxr[i][j] = max(maxr[i][j + 1],a[i][j]);
		}
//		for(int j = 1;j <= m;j ++) {
//			printf("%lld ",minr[i][j]);
//		}putchar('\n');
	}
	printf("%lld\n",solve(0,a[x1][y1] - a[x2][y2]));
	return 0;
}
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章