【題目大意】
源自拉丁方塊拼圖類型。摩天大樓是日本人發明的。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;
}
啓示:在做此題時,我覺得出題可以來個階梯:第一個層次,只給定線索一,保證行列沒有相同的數,填完即可;第二個層次,在線索一的基礎上加上線索二,按規則填完表格。造數據真是麻煩,待我哪天無聊造好數據即可將題目上線虐小白。