【题目大意】
源自拉丁方块拼图类型。摩天大楼是日本人发明的。1992年,出版商Sekai Bunka-sha在纽约举办的第一届世界益智锦标赛上向参赛者展示了一本20页的英文版益智杂志。在美国凯文•斯通(Kevin Stone)加强了这一点。
每个谜题由一个NN的网格,网格两侧有一些线索。下面的问题将会使用如下图所示的44网格。其目的是在每个格子上都安置一座摩天大楼,高度从1到4不等。因此,没有两座摩天大楼在一排或一列有相同的高度。另一个给定的线索是网格外围的数字,表示从此处的方向可以看到的摩天大楼的数量。
在上面的从左到右摆放的四个摩天大楼的例子中,用堆叠的矩形表示摩天大楼,从左边往右看,您可以看到#2、#3和#4摩天大楼。1号摩天大楼被3号摩天大楼挡住了。从右边往左看,4号摩天大楼挡住了所有其他的摩天大楼。因此线索数是3和1。
【输入】将会有6行输入。
第一行由6个数字字符串,代表一行的数字和线索。其中第一个和最后一个字符串有4个字符,表示最顶端和最底端的线索。其他的4个字符串有6个或2个字符,2个的表示一行的左边和右边的线索,6个的表示这一行从左到右的线索和网格中的数字。
第二至六行各包含2个字符,表示给定的行列坐标,如网格11 里存的1.其中11表示第一行第一列。
【输出】为每一个行列坐标输出一个正确的在表格中填入的数值。
【分析】此题类似于数独问题,但要简单一点,根据表格中数据填上去的两个线索限制,暴力搜索即可。为了让需要搜索的格子尽可能少,我们将一些特殊的数据直接填上,然后再深搜。
#include<bits/stdc++.h>
using namespace std;
queue<pair<int ,int> >q;
int mp[6][6],x[7],pos;
bool hang[5][5],lie[5][5];
bool check() { //检验填完后的表格是否满足摩天大楼的外围的看到的大厦数量的规则
for (int k=1; k<=4; k++) {
int currL = 0, currR = 0, currT = 0,currB = 0;
int LtoR = 0, RtoL = 0, TtoB = 0, BtoT = 0;
for (int j=1; j<=4; j++) {
if (mp[k][j]>currL) {
LtoR++;
currL=mp[k][j];
}
if (mp[k][4-j+1]>currR) {
RtoL++;
currR=mp[k][4-j+1];
}
if (mp[j][k]>currT) {
TtoB++;
currT=mp[j][k];
}
if (mp[4-j+1][k]>currB) {
BtoT++;
currB=mp[4-j+1][k];
}
}
if (LtoR!=mp[k][0] || RtoL!=mp[k][5]|| TtoB!=mp[0][k] || BtoT!=mp[5][k])
return false;
}
return true;
}
bool dfs(int r, int c) {
if (c==4+1) {
r++;
c=1;
}
if (r==4+1) {
if (check()){
return true;
}
return false;
}
if(mp[r][c]) if(dfs(r,c+1)) return true;
for (int i=1; i<=4; i++) {
if (!hang[r][i] && !lie[c][i]){
mp[r][c]=i;
hang[r][i] = true;
lie[c][i] = true;
if (dfs(r, c+1)){
return true;
}
mp[r][c]=0;
hang[r][i] = false;
lie[c][i] = false;
}
}
return false;
}
void fillGrid(int x,int len,int row){
int i = len ;
if(len == 2){
mp[row][0] = x/10,mp[row][5] = x%10;
}
else{
while(x){
mp[row][i] = x%10; i--; x = x/10;
}
}
}
void solve(){
fillGrid(x[0],4,0);
fillGrid(x[5],4,5);
for(int i = 1;i <= 4; i++)
fillGrid(x[i] , x[i]>100 ? 5 : 2 , i);
//处理特殊的情况 ,减少需要搜索的情况。
for(int i = 1;i<= 4; i++){
if(mp[0][i] == 1) mp[1][i] = 4;
if(mp[5][i] == 1) mp[4][i] = 4;
if(mp[0][i] == 4) mp[1][i] = 1,mp[2][i] = 2,mp[3][i] = 3,mp[4][i] = 4;
if(mp[5][i] == 4) mp[1][i] = 4,mp[2][i] = 3,mp[3][i] = 2,mp[4][i] = 1;
if(mp[i][0] == 1) mp[i][1] = 4;
if(mp[i][5] == 1) mp[i][4] = 4;
if(mp[i][0] == 4) mp[i][1] = 1,mp[i][2] = 2,mp[i][3] = 3,mp[i][4] = 4;
if(mp[i][5] == 4) mp[i][1] = 4,mp[i][2] = 3,mp[i][3] = 2,mp[i][4] = 1;
}
//标记行列中数字使用状态,hang[i][j]=true表示第i行该数字j使用了,false则表示该数还没使用
//lie[i][j]=true表示第i列该数字j使用了,false则表示未使用。
for(int i = 1;i<= 4; i++){
for(int j = 1;j<= 4; j++){
if(mp[i][j] != 0){
hang[i][mp[i][j]] = true;
lie[j][mp[i][j]] = true;
}
}
}
dfs(1,1);
}
void print(){
for(int i = 1;i <= 5; i++){
cin >> pos;
cout << mp[pos/10][pos%10] << endl;
}
}
int main(){
scanf("%d,%d,%d,%d,%d,%d",&x[0],&x[1],&x[2],&x[3],&x[4],&x[5]);
solve();
print();
return 0;
}
启示:在做此题时,我觉得出题可以来个阶梯:第一个层次,只给定线索一,保证行列没有相同的数,填完即可;第二个层次,在线索一的基础上加上线索二,按规则填完表格。造数据真是麻烦,待我哪天无聊造好数据即可将题目上线虐小白。