4*4摩天大楼问题

本文探讨了4*4网格上的摩天大楼拼图问题,要求每行和每列的楼层数唯一。通过举例说明解题思路,并提供了一种使用穷举法的C语言实现,尽管效率不高,但能有效找到解决方案。未来计划探索更优化的算法方法。

给出4*4的网格平面,要求在这个16个格子里面放入楼层分别是1、2、3、4层高的楼栋;

要求每行以及每列的1~4层楼必须且只能出现一次;

给出条件是从每个方向上所能看到的不被遮挡的楼栋数目;

Example: 

To understand how the puzzle works, this is an example of a row with 2 clues. Seen from the left side there are 4 buildings visible while seen from the right side only 1: 

 

There is only one way in which the skyscrapers can be placed. From left-to-right all four buildings must be visible and no building may hide behind another building: 


Example of a 4 by 4 puzzle with the solution: 
 

代码(我这个是用穷举的方法来做的,先算出4!一共24种组合,然后依次遍历,其实比较笨,应该有更聪明的方法,以后有空了想想):

typedef struct _AssNode{
	int Valuegroup[4];
	int LeftViewCnt;
	int RightViewCnt;
}AssNode;

int GetLeftViewCnt(int *Buff, int Sz){
	int i, j;
	int Cnt = Sz;
	
	if(Sz < 2) return Sz;
	
	/*从第二个数开始遍历,如果他前面有数比他大,说明被遮挡,返回值-1*/
	for(i=1; i<Sz; i++){
		for(j=0; j<i; j++){
			if(Buff[j] > Buff[i]){
				Cnt--;
				break;
			}
		}
	}
	return Cnt; 
}

int GetRightViewCnt(int *Buff, int Sz){
	int i, j;
	int Cnt = Sz;
	
	if(Sz < 2) return Sz;
	
	/*从倒数第二个数开始遍历,如果他后面有数比他大,说明被遮挡,返回值-1*/
	for(i=Sz-2; i>=0; i--){
		for(j=Sz-1; j>i; j--){
			if(Buff[j] > Buff[i]){
				Cnt--;
				break;
			}
		}
	}
	return Cnt; 
}

int Get4X4Assemble(AssNode *Assemble){
	int i, j, k, z;
	int cnt = 0;
	for(i=1; i<=4; i++){
		for(j=1; j<=4; j++){
			if(j == i)continue;
			for(k=1; k<=4; k++){
				if(k == i || k == j)continue;
				for(z=1; z<=4; z++){
					if(z == i || z == j || z == k)continue;
					Assemble[cnt].Valuegroup[0] = i;
					Assemble[cnt].Valuegroup[1] = j;
					Assemble[cnt].Valuegroup[2] = k;
					Assemble[cnt].Valuegroup[3] = z;
					Assemble[cnt].LeftViewCnt = GetLeftViewCnt(Assemble[cnt].Valuegroup, 4);
					Assemble[cnt].RightViewCnt = GetRightViewCnt(Assemble[cnt].Valuegroup, 4);
					cnt++;
				}
			}
		}
	}
}

/*判断数组列是否是1'2'3'4的组合*/
int IsColumeOrder(int ** buff){
	int i, j;
	int cnt = 0;
	
	for(j=0; j<4; j++){
		cnt = 0;
		for(i=1; i<=4; i++){
			if((*buff)[j] == i 
				|| (*(buff+1))[j] == i
				|| (*(buff+2))[j] == i
				|| (*(buff+3))[j] == i){
				cnt++;
			}
		}
		if(cnt != 4){
			return 0;
		}
	}
	return 1;
} 


int** SolvePuzzle (int *clues) {
	AssNode Assemble[24];
	int **out;
	int colbuf[4] = {0};
	int col; 
	int i,j,k,z;
	int ii,jj;
		
    //printf("AAA\n");
	out = malloc(sizeof(int*) * 4);
  for(i=0; i<4; i++){
    *(out+i) = malloc(sizeof(int) * 4);
  }
  
	Get4X4Assemble(Assemble); 

		/*第一行*/ 
		for(i=0; i<24; i++){
			/*找到一个符合行要求的组合*/
			if((clues[15] == 0 || Assemble[i].LeftViewCnt == clues[15]) 
				&& (clues[4] == 0 || Assemble[i].RightViewCnt == clues[4])){
				memcpy(*out, Assemble[i].Valuegroup, 4*sizeof(int));
				//printf("AAb\n");
				/*第二行*/ 
				for(j=0; j<24; j++){
					/*找到一个符合行要求的组合*/
					if((clues[14] == 0 || Assemble[j].LeftViewCnt == clues[14]) 
						&& (clues[5] == 0 || Assemble[j].RightViewCnt == clues[5])){
						memcpy(*(out+1), Assemble[j].Valuegroup, 4*sizeof(int));
						
						/*第三行*/
						for(k=0; k<24; k++){
							/*找到一个符合行要求的组合*/
							if((clues[13] == 0 || Assemble[k].LeftViewCnt == clues[13]) 
								&& (clues[6] == 0 || Assemble[k].RightViewCnt == clues[6])){
								memcpy(*(out+2), Assemble[k].Valuegroup, 4*sizeof(int));
								//printf("AAc\n");
								/*第四行*/
								for(z=0; z<24; z++){
									/*找到一个符合行要求的组合*/
									if((clues[12] == 0 || Assemble[z].LeftViewCnt == clues[12]) 
										&& (clues[7] == 0 || Assemble[z].RightViewCnt == clues[7])){
										memcpy(*(out + 3), Assemble[z].Valuegroup, 4*sizeof(int));
										
										/*校验列是否正确*/
										if(!IsColumeOrder(out)) continue;
										
										for(col=0; col<4; col++){
											colbuf[0] = out[0][col];
											colbuf[1] = out[1][col];
											colbuf[2] = out[2][col];
											colbuf[3] = out[3][col];
											//printf("AAd\n");
											if(!((clues[col] == 0 || clues[col] == GetLeftViewCnt(colbuf, 4))
												&& (clues[11-col] == 0 || clues[11-col] == GetRightViewCnt(colbuf, 4)))){
													/*校验列不正确,终止校验*/
													/*printf("Err: i=%d, j=%d, k=%d, z=%d\n", i,j,k,z);
													printf("Err: Left=%d, right=%d, clues[col]=%d, clues[11-col]=%d\n", 
														GetLeftViewCnt(colbuf, 4),GetRightViewCnt(colbuf, 4), clues[col], clues[11-col]);
													for(ii=0; ii<4; ii++){
														for(jj=0; jj<4; jj++){
															printf("%d ", out[ii][jj]);
														}
														printf("\n");
													}*/
													break; 
											}
										}
										if(col == 4){
											/*找到答案,返回*/
											/*printf("Get: i=%d, j=%d, k=%d, z=%d\n", i,j,k,z);
											for(ii=0; ii<4; ii++){
												for(jj=0; jj<4; jj++){
													printf("%d ", out[ii][jj]);
												}
												printf("\n");
											}*/
											return out;											
										}
									}
								} 
							}
						} 
					}
				} 
			}
		}

	//printf("return 0: i=%d, j=%d, k=%d, z=%d\n", i,j,k,z);
 	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值