Loading

Dancing links X

这个算法主要依据于一个高端数据结构:十字交叉双向循环链表
重复覆盖的话还需要一个\(IDA*\),主要是数据结构优化一些常数吧,复杂度要被卡还是跟爆搜一样,应该是个骗分用的算法吧(\(口胡ing\))

Upd 4.28:学完重复覆盖了,DLX并不是很适合重复覆盖,因为需要迭代加深,导致他在\(1s\)内无法处理出来超过\(100\)列,超过一百列之后远远比状压慢,所以,我本来是为了优化愤怒的小鸟才学的这个,结果学玩之后并没有做出来愤怒的小鸟(愤怒)

入门用(模板好)
入门用(有图解)
写的不错的文章1

精准覆盖\(N ,M <= 500\)适用)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register int 

using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 5e2 + 10 ;
const int S = 5e3 + 10 + N ; //数据极限的话由于要存表头,所以需要多开

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 , m ;

struct DLX{
	int n , m ;  // n 行 m 列 
	int size ; // 一共size个1
	int l [S] , r [S] , u [S] , d [S] , row [S] , col [S] ;  //点的基本六个元素 up,down,left,right指针,点属于第row行第col列
	int head [N] , cvr [N] ; //记录行的选择(这一行的最后一个元素来实现加点),列的覆盖(cvr)
	int ans [N] ;// 答案栈
	
	void pre( int ns , int ms ){
		n = ns , m = ms ;
		for( R i = 0 ; i <= m ; i ++ ){  //初始化表头
			cvr [i] = 0 ;
			u [i] = d [i] = i ; 
			l [i] = i - 1 , r [i] = i + 1 ;
		} // 横向连接,纵向是空的
		
		r [m] = 0 ; l [0] = m ;  // 循环链表
		size = m ; // 已有m个表头节点
		for( R i = 1 ; i <= n ; i ++ ) 
			head [i] = -1 ;
	}
	
	void ins( int roww , int coll ){  // 插入一个点
		size ++ ; cvr [coll] ++ ; // 加入一个点,列的覆盖情况情况更改
		col [size] = coll , row [size] = roww ;
//		printf( "THIS IS%d\n" , size ) ;
//		printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
		d [size] = d [coll] ;
		u [d [coll]] = size ;	 
		u [size] = coll ; 
		d [coll] = size ;
		if( head [roww] == -1 ) //如果这一行没有点,初始化这一行
		head [roww] = l [size] = r [size] = size ; 
		else{
			r [size] = r [head [roww]] ;
			l [r [head [roww]]] = size ;
			l [size] = head [roww] ; 
			r [head [roww]] = size ; 	
		}
///		printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
//		printf( "U %5d D %5d L %5d R %5d\n" , u [size] , d [size] , l [size] , r [size] ) ;
//		printf( "U %5d D %5d L %5d R %5d\n" , u [size - 1] , d [size - 1] , l [size - 1] , r [size - 1] ) ;
	}
		
	void del( int c ){  //删掉第c列
		l [r [c]] = l [c] ;
		r [l [c]] = r [c] ;
		for( R i = d [c] ; i != c ; i = d [i] ) // ,枚举这一列的所有节点
			for( R j = r [i] ; j != i ; j = r [j] )  // 枚举这个节点所在的一行的所有节点
			u [d [j]] = u [j] , d [u [j]] = d [j] ,	cvr [col [j]] -- ; //删除之
	}
	
	void bak( int c ){ // 复原第c列
		for( R i = u [c] ; i != c ; i = u [i] )
			for( R j = l [i] ; j != i ; j = l [j] )
			d [u [j]] = j , u [d [j]] = j , cvr [col [j]] ++ ;			
		l [r [c]] = r [l [c]] = c ;
	}
	
	bool dance( int dep ){ // 已经搞定dep行
		if( r [0] == 0 ){ //全部覆盖
			for( R i = 0 ; i < dep ; i ++ )
				printf( "%d " , ans [i] ) ;
			return 1 ; 
		} 
		int c = r [0] ; // 指向第一列
		for( R i = r [0] ; i != 0 ; i = r [i] ) //表头指针枚举
			if( cvr [i] < cvr [c] ) c = i ; // 选择列中元素最少的操作
			
		del( c ) ;
		
		for( R i = d [c] ; i != c ; i = d [i] ){ //枚举这一列
			ans [dep] = row [i] ; // 压入答案栈
			for( R j = r [i] ; j != i ; j = r [j] )
				del( col [j] ) ; //诛这一行里元素的九族
				
			if( dance( dep + 1 ) ) return 1 ; 
			
			for( R j = l [i] ; j != i ; j = l [j] )
				bak( col [j] ) ; //冤杀,再请回来
		}
		
		bak( c ) ; // 无法继续加深,回溯
		
		return 0 ;
	}
} a ;

void sc(){
	n = read() , m = read() ; a.pre( n , m ) ;
	for( R i = 1 ; i <= n ; i ++ )
		for( R j = 1 ; j <= m ; j ++ )
			if( read() ) a.ins( i , j ) ;
}

void work(){
	if( !a.dance( 0 ) ) puts( "No Solution!" ) ;	
	else return ; 
}

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

重复覆盖\(N, M <= 100\)适用)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register int 

using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 5e2 + 10 ;
const int S = 5e3 + 10 + N ; //数据极限的话由于要存表头,所以需要多开

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 , m , dep ;

struct DLX{
	int n , m ;  // n 行 m 列 
	int size ; // 一共size个1
	int l [S] , r [S] , u [S] , d [S] , row [S] , col [S] ;  //点的基本六个元素 up,down,left,right指针,点属于第row行第col列
	int head [N] , cvr [N] ; //记录行的选择(这一行的最后一个元素来实现加点),列的覆盖(cvr)
	int ans [N] , top ;// 答案栈
	bool fg [N] ; //估价用
	
	void pre( int ns , int ms ){
		n = ns , m = ms ;
		for( R i = 0 ; i <= m ; i ++ ){  //初始化表头
			cvr [i] = 0 ;
			u [i] = d [i] = i ; 
			l [i] = i - 1 , r [i] = i + 1 ;
		} // 横向连接,纵向是空的
		
		r [m] = 0 ; l [0] = m ;  // 循环链表
		size = m ; // 已有m个表头节点
		for( R i = 1 ; i <= n ; i ++ ) 
			head [i] = -1 ;
	}
	
	void ins( int roww , int coll ){  // 插入一个点
		size ++ ; cvr [coll] ++ ; // 加入一个点,列的覆盖情况情况更改
		col [size] = coll , row [size] = roww ;
//		printf( "THIS IS%d\n" , size ) ;
//		printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
		d [size] = d [coll] ;
		u [d [coll]] = size ;	 
		u [size] = coll ; 
		d [coll] = size ;
		if( head [roww] == -1 ) //如果这一行没有点,初始化这一行
		head [roww] = l [size] = r [size] = size ; 
		else{
			r [size] = r [head [roww]] ;
			l [r [head [roww]]] = size ;
			l [size] = head [roww] ; 
			r [head [roww]] = size ; 	
		}
///		printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
//		printf( "U %5d D %5d L %5d R %5d\n" , u [size] , d [size] , l [size] , r [size] ) ;
//		printf( "U %5d D %5d L %5d R %5d\n" , u [size - 1] , d [size - 1] , l [size - 1] , r [size - 1] ) ;
	}
		
	void del( int c ){  //删掉元素所在的列
		for( R i = d [c] ; i != c ; i = d [i] ) // ,枚举这一列的所有节点
			l [r [i]] = l [i] , r [l [i]] = r [i] ;
	}
	
	void bak( int c ){ // 复原元素所在的列
		for( R i = u [c] ; i != c ; i = u [i] ) 
			l [r [i]] = i , r [l [i]] = i ;
	}
	
	inline int guess(){ //估价函数
		//强剪枝。这个 剪枝利用的思想是A*搜索中的估价函数。
		//即,对于当前的递归深度K下的矩阵,估计其最好情况下(即最少还需要多少步)才能出解。
		//也就是,如果将能够覆盖当 前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度。
		//重复此操作直到所有列全部出解的深度是多少。如果当前深度加上这个估价函数返 回值,其和已然不能更优(也就是已经超过当前最优解),则直接返回,不必再搜。
		memset( fg , 0 , sizeof ( fg ) ) ;
		int cnt = 0 ;
		for( R i = r [0] ; i ; i = r [i] ){
			if( fg [i] ) continue ;  
			cnt ++ ;
			for( R j = d [i] ; j != i ; j = d [j] )
				for( R k = r [j] ; k != j ; k = r [k] )
					fg [col [k]] = 1 ;
		} 
		return cnt ;
	}
		
         /*重复覆盖
         	1、如果矩阵为空,得到结果,返回
        	2、从矩阵中选择一列,以选取最少元素的列为优化方式
       	  	3、删除该列及其覆盖的行
         	4、对该列的每一行元素:删除一行及其覆盖的列,
         	5、进行下一层搜索,如果成功则返回
         	6、恢复现场,跳至4
         	7、恢复所选择行
         */
	
	//以上注释均转载于第一个链接
	
	bool dance( ){ 
//		printf( "GUESS%d TOP%d DEP%d\n" , guess() , top , dep ) ;
		if( guess() + top > dep ) return 0 ;
		if( r [0] == 0 ) return 1 ;
		int c = r [0] ; // 指向第一列
		for( R i = r [0] ; i != 0 ; i = r [i] ) //表头指针枚举
			if( cvr [i] < cvr [c] ) c = i ; // 选择列中元素最少的操作
		
//		if( !cvr [c] ) return 0 ;
		
		for( R i = d [c] ; i != c ; i = d [i] ){ //枚举这一列
			ans [++ top] = row [i] ; // 压入答案栈
			del ( i ) ;
			
			for( R j = r [i] ; j != i ; j = r [j] ) del( j ) ; 
				
			if( dance() ) return 1 ; 
			
			for( R j = l [i] ; j != i ; j = l [j] ) bak( j ) ; 
			
			bak( i ) ;
			top -- ;
		}
		
		return 0 ;
	}
} a ;

void sc(){
	n = read() , m = read() ; a.pre( n , m ) ;
	for( R i = 1 ; i <= n ; i ++ )
		for( R j = 1 ; j <= m ; j ++ )
			if( read() ) a.ins( i , j ) ;
}

void work(){
	dep = 1 ;
	while( !a.dance() ) /*	printf( "%d\n" , dep ) ,*/ dep ++ ; // 迭代加深
	printf( "%d\n" , dep ) ;
	for( R i = 1 ; i <= dep ; i ++ ) printf( "%d " , a.ans [i] ) ;
}

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

愤怒的小鸟
这个题预处理出来抛物线之后(最多不会超过\(n*n/2\)条,如果开数组记录的话还可以更少,只不过预处理也要花些时间)就是一个重复覆盖问题,打个板子就行了
Upd;4/28:太寂寞了,发现所有抛物线都处理出来有200多条(极限数据),即使用哈希去重也有100多条,\(T=5\)实测\(4.5s\)跑出来,决定用这个算法之前一定要看好数据范围,超过\(50\)就慎用了

还是说一下思路吧:预处理出来每一条抛物线能砸多少鸟,最后转化为重复覆盖问题用状压,对于每一个状态\(i\)枚举\(i|j,j\in抛物线\),取最小值就行了

状压正解

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <cmath>
#include <iostream>
//#include <queue>
#define R register int
//#define int long long

using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 20 ;
const int iinf = 0x7fffffff ;
const int S = 1e7 ;
const D li = 1e-8 ;

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 top , pot ;
int t , n , lg [1 << N] , f [1 << N] , rks [N][N] ;
struct BIRD{ D x , y ; } a [N] ;  
 //struct OP{ D x , y ; int cnt [1 << N] ; } f [N] ; 


inline void debug( int x ) { cout << bitset <11> ( x ) << endl ; }

inline int lb( int x ){ return x & -x ; }

void sc(){
	t = read() ;
}

// 1 3 0 1.11 1.41 2.34 1.79 2.98 1.49
// 1 2 0 1.41 2.00 1.73 3.00

inline D geta( D x , D y , D xx , D yy ){
	D a = x * yy - xx * y ;
	D b = x * xx * ( xx - x ) ;
	return ( a / b ) ;
}

inline D getb( D x , D y , D xx , D yy ){
	D a = x * x * yy - xx * xx * y ;
	D b = x * xx * ( x - xx ) ;
	return ( a / b ) ;
}

inline bool fc( D a , D b , D x , D y ){
	if( fabs( a * x * x + b * x - y ) < li ) return 1 ;
	else return 0 ;
}

void work(){
	while( t -- ){
		
		n = read() , read() ; 
		for( R i = 0 ; i < n ; i ++ ) scanf( "%lf%lf" , & a [i].x , & a [i].y ) ;
		memset( f , 0x3f , sizeof( f ) ) ; f [0] = 0 ; pot = 0 ;
		memset( rks , 0 , sizeof( rks ) ) ;
		for( R i = 0 ; i < n ; i ++ ){
			rks [i][i] = 1 << i ;
			for( R j = 0 ; j < n ; j ++ ){
				if( fabs( a [i].x - a [j].x ) < li ) continue ;
				D as = geta( a [i].x , a [i].y , a [j].x , a [j].y ) ;
				if( as >= 0 ) continue ;
				D bs = getb( a [i].x , a [i].y , a [j].x , a [j].y ) ;
	//			printf( "%d %d %lf %lf\n" , i , j , as , bs ) ;
				for( R k = 0 ; k < n ; k ++ ){
					if( fc( as , bs , a [k].x , a [k].y ) )
					rks [i][j] += 1 << k ;
				}
			}
		}
		
		
		for( R i = 0 ; i < ( ( 1 << n ) - 1 ) ; i ++ )
			for( R j = 0 ; j < n ; j ++ )
				for( R js = 0 ; js < n ; js ++ )
					f [i | rks [j][js]] = min( f [i | rks [j][js]] , f [i] + 1 ) ;	
				
		
		printf( "%d\n" , f [( 1 << n ) - 1] ) ;
	} 

}

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

DLX哈希优化TLE90分



#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#define R register int 

using namespace std ;
typedef long long L ;
typedef double D ;
typedef unsigned long long G ;
const int N = 4e2 + 10 ;
const int H = 20 ;
const int S = 2e4 + 10 + N ; //数据极限的话由于要存表头,所以需要多开
const D li = 1e-8 ;
const int P = 131 ;

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 nss , dep , uu ;
bool fggg , fgg [N] ; 
int T ; int rks [N][H] ;
vector<G> sb ;
G Has [N] ; //哈希去重剪枝

struct DLX{
	int n , m ;  // n 行 m 列 
	int size ; // 一共size个1
	int l [S] , r [S] , u [S] , d [S] , row [S] , col [S] ;  //点的基本六个元素 up,down,left,right指针,点属于第row行第col列
	int head [N] , cvr [H] ; //记录行的选择(这一行的最后一个元素来实现加点),列的覆盖(cvr)
	int ans [N] , top ;// 答案栈
	bool fg [N] ; //估价用
	
	void clear(){
		n = 0 ; m = 0 ; size = 0 ; top = 0 ;
		memset( l , 0 , sizeof( l ) ) ;
		memset( r , 0 , sizeof( l ) ) ;
		memset( u , 0 , sizeof( l ) ) ;
		memset( d , 0 , sizeof( l ) ) ;
		memset( row , 0 , sizeof( l ) ) ;
		memset( col , 0 , sizeof( l ) ) ;
		memset( head , 0 , sizeof( head ) ) ;
		memset( cvr , 0 , sizeof( cvr ) ) ;
		memset( ans , 0 , sizeof( ans ) ) ;
	}
	
	
	void pre( int ns , int ms ){
//		printf( "%d\n" , ms ) ;
		n = ns , m = ms ;
		for( R i = 0 ; i <= m ; i ++ ){  //初始化表头
			cvr [i] = 0 ;
			u [i] = d [i] = i ; 
			l [i] = i - 1 , r [i] = i + 1 ;
		} // 横向连接,纵向是空的
		
		r [m] = 0 ; l [0] = m ;  // 循环链表
		size = m ; // 已有m个表头节点
		for( R i = 1 ; i <= n ; i ++ ) 
			head [i] = -1 ;
	}
	
	void ins( int roww , int coll ){  // 插入一个点
//		printf( "INS%d %d\n" , roww , coll ) ;
		size ++ ; cvr [coll] ++ ; // 加入一个点,列的覆盖情况情况更改
		col [size] = coll , row [size] = roww ;
//		printf( "THIS IS%d\n" , size ) ;
//		printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
		d [size] = d [coll] ;
		u [d [coll]] = size ;	 
		u [size] = coll ; 
		d [coll] = size ;
		if( head [roww] == -1 ) //如果这一行没有点,初始化这一行
		head [roww] = l [size] = r [size] = size ; 
		else{
			r [size] = r [head [roww]] ;
			l [r [head [roww]]] = size ;
			l [size] = head [roww] ; 
			r [head [roww]] = size ; 	
		}
///		printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
//		printf( "U %5d D %5d L %5d R %5d\n" , u [size] , d [size] , l [size] , r [size] ) ;
//		printf( "U %5d D %5d L %5d R %5d\n" , u [size - 1] , d [size - 1] , l [size - 1] , r [size - 1] ) ;
	}
		
	void del( int c ){  //删掉元素所在的列
		for( R i = d [c] ; i != c ; i = d [i] ) // ,枚举这一列的所有节点
			l [r [i]] = l [i] , r [l [i]] = r [i] ;
	}
	
	void bak( int c ){ // 复原元素所在的列
		for( R i = u [c] ; i != c ; i = u [i] ) 
			l [r [i]] = i , r [l [i]] = i ;
	}
	
	inline int guess(){ //估价函数
		//强剪枝。这个 剪枝利用的思想是A*搜索中的估价函数。
		//即,对于当前的递归深度K下的矩阵,估计其最好情况下(即最少还需要多少步)才能出解。
		//也就是,如果将能够覆盖当 前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度。
		//重复此操作直到所有列全部出解的深度是多少。如果当前深度加上这个估价函数返 回值,其和已然不能更优(也就是已经超过当前最优解),则直接返回,不必再搜。
		memset( fg , 0 , sizeof ( fg ) ) ;
		int cnt = 0 ;
		for( R i = r [0] ; i ; i = r [i] ){
			if( fg [i] ) continue ;  
			cnt ++ ;
			for( R j = d [i] ; j != i ; j = d [j] )
				for( R k = r [j] ; k != j ; k = r [k] )
					fg [col [k]] = 1 ;
		} 
		return cnt ;
	}
		
         /*重复覆盖
         	1、如果矩阵为空,得到结果,返回
        	2、从矩阵中选择一列,以选取最少元素的列为优化方式
       	  	3、删除该列及其覆盖的行
         	4、对该列的每一行元素:删除一行及其覆盖的列,
         	5、进行下一层搜索,如果成功则返回
         	6、恢复现场,跳至4
         	7、恢复所选择行
         */
	
	//以上注释均转载于第一个链接
	
	bool dance( ){ 
//		printf( "GUESS%d TOP%d DEP%d\n" , guess() , top , dep ) ;
		if( guess() + top > dep ) return 0 ;
		
		if( r [0] == 0 ) return 1 ;
		int c = r [0] ; // 指向第一列
		for( R i = r [0] ; i != 0 ; i = r [i] ) //表头指针枚举
			if( cvr [i] < cvr [c] ) c = i ; // 选择列中元素最少的操作
//		printf( "C%d %d\n" , c , cvr [c] ) ;
		if( !cvr [c] ) return 0 ;
		
		for( R i = d [c] ; i != c ; i = d [i] ){ //枚举这一列
			
			ans [++ top] = row [i] ; // 压入答案栈
//			printf( "PASS%d %d %d\n" , dep , top , i ) ;
			del ( i ) ;
			
			for( R j = r [i] ; j != i ; j = r [j] ) del( j ) ; 
				
			if( dance() ) return 1 ; 
			
			for( R j = l [i] ; j != i ; j = l [j] ) bak( j ) ; 
			
			bak( i ) ;
			top -- ;
		}
		
		return 0 ;
	}
	
} a ;

struct POINT{ D x , y ; } p [N] ;

void sc(){
	T = read() ;
}

inline D geta( D x , D y , D xx , D yy ){
	D a = x * yy - xx * y ;
	D b = x * xx * ( xx - x ) ;
	return ( a / b ) ;
}

inline D getb( D x , D y , D xx , D yy ){
	D a = x * x * yy - xx * xx * y ;
	D b = x * xx * ( x - xx ) ;
	return ( a / b ) ;
}

inline bool fc( D a , D b , D x , D y ) {
	if( fabs( a * x * x + b * x - y ) < li ) return 1 ; 
	else return 0 ;
}

inline G qpow( int p ){
	int ans = 1 ; int base = P ;
	while( p){
		if( p & 1 ) ans *= base ;
		base *= P ;
		p >>= 1 ;
	} return ans ;
}

void debug(){
	printf( "UU%d\n" , uu ) ;
	for( R i = 1 ; i <= uu ; i ++ ){
		printf( "\nI%d\n" , i ) ;
		for( R j = 1 ; j <= rks [i][0] ; j ++ )
			printf( "%d " , rks [i][j] ) ;
	}
}

inline bool check( int p ){
	for( R i = 0 ; i < sb.size() ; i ++ )
	if( Has [p] == sb [i] ) return 1 ;
	return 0 ;
}

void mk(){
	fggg = 0 ; memset( fgg , 0 , sizeof( fgg ) ) ; sb.clear() ;
	memset( Has , 0 , sizeof( Has ) ) ;
	a.clear() ; memset( rks , 0 , sizeof( rks ) ) ; uu = 0 ;
	nss = read() ; read() ;
	for( R i = 1 ; i <= nss ; i ++ ) scanf( "%lf%lf" , & p [i].x , & p [i].y ) ;
	
	for( R i = 1 ; i <= nss ; i ++ ){
		rks [++ uu][++ rks [uu][0]] = i ; 
		for( R j = 1 ; j <= nss ; j ++ ){
			 if( i == j ) continue ;
			if( fabs( p [i].x - p [j].x ) < li ) continue ;
			D as = geta( p [i].x , p [i].y , p [j].x , p [j].y ) ;
			if( as >= 0 ) continue ;
			else rks [++ uu][++ rks [uu][0]] = i ; 
			D bs = getb( p [i].x , p [i].y , p [j].x , p [j].y ) ;
	//		printf( "%d %d %lf %lf\n" , i , j , as , bs ) ;
			rks [uu][++ rks [uu][0]] = j ;
	//		printf( "IJ%d %d\n" , i , j ) ;
			for( R k = 1 ; k <= nss ; k ++ ){
				if( k == i || k == j ) continue ;
				
				if( fc( as , bs , p [k].x , p [k].y ) ) { //printf( "K%d\n" , k ) ;
				rks [uu][++ rks [uu][0]] = k ; }
			}
		}				
		
	} 
//	debug() ;
	int uuu = uu ;
//	printf( "%d\n" , uu ) ;
	for( R i = 1 ; i <= uu ; i ++ ){
		sort( rks [i] + 1 , rks [i] + 1 + rks [i][0] ) ;
		for( R j = 1 ; j <= rks [i][0] ; j ++ ) 
		Has [i] += rks [i][j] * qpow( j ) ;
//		printf( "%llu\n" , Has [i] ) ; 
 	}
 	
 	for( R i = 1 ; i <= uu ; i ++ ){
		if( !check( i ) ) sb.push_back( Has [i] ) ;
		else fgg [i] = 1 , uuu -- ;
	}
 	
// 	for( R i = 1 ; i <= uu ; i ++ ) cout << i << " " << fgg [i] << endl ;
	
//	printf( "UUU%d\n" , uuu ) ;
	
	if( !uu ) { fggg = 1 ; return ; }
	a.pre( uuu , nss ) ;
	int uuuu = 0 ;
	for( R i = 1 ; i <= uu ; i ++ ){
		if( fgg [i] ) { /* printf( "%d\n" , i ) ; */ continue ; }
		uuuu ++ ;
		for( R j = 1 ; j <= rks [i][0] ; j ++ ){
			a.ins( uuuu , rks [i][j] ) ;
		}
	}
	
}

void work(){
	while( T -- ){
		mk() ; if( fggg ){ printf( "%d\n" , nss ) ; continue ; }
		dep = 1 ;
		while( !a.dance() ) /*printf( "DEP%d\n" , dep ) , */ dep ++ ; // 迭代加深
		printf( "%d\n" , dep ) ;
	}
}

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

哀悼因寂寞优化逝去的三天

posted @ 2021-04-27 18:35  Soresen  阅读(100)  评论(1)    收藏  举报