求给定的n(1<=n<=16)个正整数边长的小正方形(每个边长<=10)能否恰好无重叠拼成一个边长为s的大正方形。
思路和方法:这道题不可以用贪心算法,有特殊的情况会覆盖不到。
思路:类似于欧罗斯方块,每次选一个方块放到最低点。
数据结构解释:
struct piece{
int size;//边长
int num;//数量
int remine;//剩余
} //存储每个蛋糕碎片的信息
struct piece pieces[];//蛋糕碎片的数组
struct bottom{
int height;//这段底部的高度
int length;//这段底部的长度
} //底部
struct bottom bottoms[];//由已放正方形组成的底部
bool insert(struct piece pieces[], struct bottom bottoms[]){
ok = false;
for(遍历pieces[]中剩余的piece){
if(最低点可以放入){
for( 选择放入位置 ){
if(可以放入){
在指定位置放置piece
修订bottoms[]
if(填满)
return true;
ok = insert();
if( ok )
return true;
}
}
}
}
return ok;
}
struct piece{
int size;//边长
int num;//数量
int remine;//剩余
};
struct bottom{
int height;
int length;
struct bottom *nextptr;
};
int piece_type = 0;
int cake;
int empty(struct piece pieces[]){
int i;
for( i=0; i<piece_type; ++i ){
if(pieces[i].remine>0)
return 0;
}
return 1;
}
int insert(struct piece pieces[], struct bottom *bottoms){
struct piece pieces_[20];
struct bottom *bottoms_, *ptr, *ptr_, *b_index, *b_index_, *pre_index, *pre_index_, *tptr;
int ok = 0;
int i,j,k,lowest,width;
for( i=0; i<piece_type; ++i ){
if(pieces[i].remine>0){
//printf("try: %d ",pieces[i].size);//debugggggggggggggggggggggggggg
//找到最低点
//printf("[%d,%d]-",bottoms->height,bottoms->length);//debugggggggggggggggggggggggggg
for(ptr = b_index = bottoms,lowest=bottoms->height,pre_index = NULL; ptr->nextptr; ptr = ptr->nextptr){
//printf("[%d,%d]-",ptr->nextptr->height,ptr->nextptr->length);//debugggggggggggggggggggggggggg
if(ptr->nextptr->height<lowest){
b_index = ptr->nextptr;
pre_index = ptr;
lowest = ptr->nextptr->height;
}
}
//printf(" height:%d length:%d\n",b_index->height,b_index->length);//system("pause");//debugggggggggggggggggggggggggg
for(j=0; j<b_index->length; ++j){//选择放入位置
if( b_index->length - j >= pieces[i].size && cake - b_index->height >= pieces[i].size ){//选择的位置可以放下蛋糕碎片
//复制bottoms[] pieces[]
for( k=0; k<piece_type; ++k ){
pieces_[k].size = pieces[k].size;
pieces_[k].num = pieces[k].num;
pieces_[k].remine = pieces[k].remine;
}
for( ptr = bottoms, pre_index_ = b_index_ = ptr_ = bottoms_ = tptr = NULL; ptr; ptr = ptr->nextptr ){
ptr_ = (struct bottom *)malloc(sizeof(struct bottom));
ptr_->height = ptr->height;
ptr_->length = ptr->length;
ptr_->nextptr = NULL;
if( bottoms_ == NULL ) tptr = bottoms_ = ptr_;//处理头指针
else{
tptr ->nextptr = ptr_; tptr = ptr_;
}
//复制b_index 和 pre_index
if(ptr == b_index){ b_index_ = ptr_; }
if(ptr == pre_index){ pre_index_ = ptr_; }
}
--pieces_[i].remine;//在指定位置放置piece
//修订bottoms
if(j==0){
b_index_->height = pieces_[i].size + b_index_->height;
width = b_index_->length;
b_index_->length = pieces_[i].size;
if( pre_index_ && pre_index_->height == b_index_->height ){//如果和左边的bottom连成一片
pre_index_->length = pre_index_->length + b_index_->length;
tptr = b_index_;
b_index_ = pre_index_;
b_index_->nextptr = tptr->nextptr;
free(tptr);
}
if( width - pieces_[i].size != 0 ){//放入后还留有空隙
ptr = (struct bottom *)malloc(sizeof(struct bottom));
ptr->height = b_index_->height - pieces_[i].size;
ptr->length = width - pieces_[i].size;
ptr->nextptr = b_index_->nextptr;
b_index_->nextptr = ptr;
} else{
if( b_index_->nextptr && b_index_->height == b_index_->nextptr->height ){//和右边连成一片
b_index_->length = b_index_->length + b_index_->nextptr->length;
tptr = b_index_->nextptr;
b_index_->nextptr = tptr->nextptr;
free(tptr);
}
}
} else{
width = b_index_->length;
b_index_->length = j;
ptr = (struct bottom *)malloc(sizeof(struct bottom));
ptr->height = b_index_->height + pieces_[i].size;
ptr->length = pieces_[i].size;
ptr->nextptr = b_index_->nextptr;
b_index_->nextptr = ptr;
if( width - pieces_[i].size -j != 0 ){
ptr = (struct bottom *)malloc(sizeof(struct bottom));
ptr->height = b_index_->height;
ptr->length = width - pieces_[i].size - j;
ptr->nextptr = b_index_->nextptr->nextptr;
b_index_->nextptr->nextptr = ptr;
} else{
if(b_index_->nextptr->nextptr && b_index_->nextptr->height == b_index_->nextptr->nextptr->height){//和右边连成一片
b_index_->nextptr->length = b_index_->nextptr->length + b_index_->nextptr->nextptr->length;
tptr = b_index_->nextptr->nextptr;
b_index_->nextptr->nextptr = tptr->nextptr;
free(tptr);
}
}
}
if( bottoms_->nextptr == NULL && bottoms_->height == cake && empty(pieces_))//如果装满
return 1;
//递归
ok = insert(pieces_, bottoms_);
if( ok )//找到一个可以的分支
return 1;
} else{
break;
}
}
}
}
return ok;
}
int main(){
int n,m,i,j,k,find,temp;
struct piece pieces[20];
struct bottom *bottoms;
scanf("%d",&n);
for( i=0; i<n; ++i ){
for(i=0;i<20;++i){ pieces[i].num = pieces[i].remine = 0; }//初始化
scanf("%d %d",&cake, &m);//存放蛋糕的边长,碎片的块数
piece_type = 0;//初始化碎片种类
for( j=0; j<m; ++j ){//写入碎片表
scanf("%d",&temp);
find = 0;
for( k=0; k<piece_type; ++k ){
if( pieces[k].size == temp ){
find = 1;
++pieces[k].num;
++pieces[k].remine;
break;
}
}
if(find == 0){
pieces[piece_type].num = pieces[piece_type].remine = 1;
pieces[piece_type].size = temp;
++piece_type;
}
}
bottoms = (struct bottom *)malloc(sizeof(struct bottom));
bottoms->height = 0; bottoms->length = cake;//初始化bottoms
bottoms->nextptr = NULL;
if(insert(pieces,bottoms)) printf("ok\n");
else printf("failed\n");
system("pause");
}
}