Loading

题解—P2218 [HAOI2007]覆盖问题

一道不错的题,主要就是一个思路点,想到就行了,想不到就一直卡着。
看完题解之后发现挺简单,实际上自己挣扎半天也咩有想到。

一开始想类比成一维之后贪心,后来被同机房大佬 \(hack\) 掉了。

solution

首先二分答案,单调行很好证明,因为大矩形包含小矩形,现在的问题就是 \(check\) 函数

其实一个突破口就是只用三个矩形覆盖。

一维里面用几个矩形理论上都是通过输入给出的,所以一般的题的变量他通过常量给出来,可能就是突破口

对于题目中所有的点,肯定可以用一个大矩形套住,我们称之为“外接矩形”

思考一下可以发现,外接矩形的边上肯定有点,那么也一定有三个矩形的边(放边的原因是多放到外面肯定不如放到里面优

一个外接矩形有四条边,这就意味着,对于我们的三个矩形,肯定有一个要和两条边重合(也就是和一个角重合)

我们枚举是哪个角,之后把它范围内的点覆盖上,然后问题就变成了一个子问题(因为小于等于三个矩形都必须覆盖角,所以枚举角就可以了)。

然后经典的搜索和回溯就可以写完 \(check\) 函数。

code

(放着么美的代码有没有亮瞎您的眼?)

#include <cstring>
#include <algorithm>
#include <cstdio>
#define mp make_pair
#define R register int
#define int long 
#define printf Ruusupuu = printf

int Ruusupuu ;

using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 2e4 + 10 ;
const int Inf = 1e9 + 10 ;

inline int read(){
	int w = 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
	return fg ? -w : w ; 
}

int n , mid , x [N] , y [N] ;
bool cvr [N] ;
int st [N] , top ;

void sc(){
	n = read() ;
	for( R i = 1 ; i <= n ; i ++ ) x [i] = read() , y [i] = read() ;
} 

inline void rmcvr( int x ){ while( top > x ) cvr [st [top --]] = 0 ; }

inline bool check( int now ){
	if( top == n ) return 1 ;
	if( now == 4 ) return 0 ; 
	
	int l = Inf , r = -Inf , u = -Inf , d = Inf , bg = top ;
	bool fg = 0 ;	
	
	for( R i = 1 ; i <= n ; i ++ ) if( !cvr [i] ){
		l = min( l , x [i] ) , r = max( r , x [i] ) ;		
		d = min( d , y [i] ) , u = max( u , y [i] ) ;
	}
	
//	printf( "%ld %ld %ld %ld %ld\n" , now , l , r , u , d ) ;
	
	int ix , iy ;
	
	// left up 
	ix = l , iy = u ;
	for( R i = 1 ; i <= n ; i ++ ) if( !cvr [i] ){
		if( x [i] >= ix && x [i] <= ix + mid && y [i] >= iy - mid && y [i] <= iy ) cvr [i] = 1 , st [++ top] = i ;
	} fg |= check( now + 1 ) ; rmcvr( bg ) ; if( fg ) return 1 ;
	
	// left down 
	ix = l , iy = d ;
	for( R i = 1 ; i <= n ; i ++ ) if( !cvr [i] ){
		if( x [i] >= ix && x [i] <= ix + mid && y [i] >= iy && y [i] <= iy + mid ) cvr [i] = 1 , st [++ top] = i ;
	} fg |= check( now + 1 ) ; rmcvr( bg ) ; if( fg ) return 1 ;
	
	// right up
	ix = r , iy = u ;
	for( R i = 1 ; i <= n ; i ++ ) if( !cvr [i] ){
		if( x [i] >= ix - mid && x [i] <= ix && y [i] >= iy - mid && y [i] <= iy ) cvr [i] = 1 , st [++ top] = i ;
	} fg |= check( now + 1 ) ; rmcvr( bg ) ; if( fg ) return 1 ; 
	
	// right down
	ix = r , iy = d ;
	for( R i = 1 ; i <= n ; i ++ ) if( !cvr [i] ){
		if( x [i] >= ix - mid && x [i] <= ix && y [i] >= iy && y [i] <= iy + mid ) cvr [i] = 1 , st [++ top] = i ;
	} fg |= check( now + 1 ) ; rmcvr( bg ) ; if( fg ) return 1 ;
	
	return 0 ;
}

void work(){
	
	int lside = 1 , rside = (int) 2e9 + 10 , ans ;
	
	while( lside <= rside ){
		mid = ( 0ll + lside + rside ) >> 1 ;
		if( check( 1 ) ) ans = mid , rside = mid - 1 ; 
		else lside = mid + 1 ;
	} printf( "%ld\n" , ans ) ;
}

signed main(){	
	sc() ;
	work() ;
	return 0 ;
} 

posted @ 2021-07-04 15:43  Soresen  阅读(58)  评论(0)    收藏  举报