百度之星2017 初賽 迷宮出逃(HDU 6111)
阿新 • • 發佈:2017-10-06
truct cst bre 不能 影響 ble 格子 機會 accepted
不同於一般的迷宮,魔王在迷宮裏安置了機關,一旦觸碰,那麽四個方向所在的格子,將翻轉其可達性(原先可通過的格子不可通過,反之亦然,機關可以反復觸發)。為了防止小明很容易地出逃,魔王在臨走前把鑰匙丟在了迷宮某處,只有拿到鑰匙,小明才能開門在出口處離開迷宮。
萬般無奈之下,小明想借助聰明的你,幫忙計算是否有機會離開這個迷宮,最少需要多少時間。(每一單位時間只能向四鄰方向走一步)
下面 T 組數據,對於每組數據:
第一行是兩個數字 n, m(2 < n * m <= 64),表示迷宮的長與寬。
接下來 n 行,每行 m 個字符,‘.’表示空地可以通過,‘x’表示陷阱,‘*’表示機關,‘S’代表起點,‘E’代表出口,‘K’表示鑰匙(保證存在且只有一個)。
Case #i:
然後輸出一行,僅包含一個整數,表示最少多少步能夠拿到鑰匙並走出迷魂陣,如果不能則打出-1。
題目分析: M*N最大64,很容易就想到狀態壓縮,又因為是找到終點的最近距離,那麽肯定使用BFS。 狀態壓縮也很基礎,假設N行M列,從第一行第一列是二進制第一位,第一行第二列是二進制第二位,第一行第三列是二進制第三位 ,第二行第一列是二進制第M+1位,第x行第y列是二進制的第(x-1)*m+y位。每一位是1表示狀態反轉,每一位狀態是0表示狀態正常。 如此,就可以很輕松的,把狀態用一個64位的無符號整數儲存下來了。 然後有沒有鑰匙是2種狀態,0表示有,1表示沒有。 找到出口看鑰匙狀態是0還是1即可 每一位能不能走看那個位置原本的圖,和狀態是否改變即可。 那麽問題來了,bfs必須要判重,也就是要有一個哈希表,存儲這個狀態到底有沒有被訪問過。單純的反轉狀態就有2^64次方個,那麽,用一個數組去裸存肯定不行。比賽時,我就想到了map,結果超了內存。內存限制32MB,太小了。 然後評測機就一直卡著,然後比賽就這麽結束了。 其實,比賽完之後仔細想想,狀態絕對不會出現很多,因為圖太小了,我就寫了個hash表,建立了一個10000的queue數組,取每個狀態的坐標,反轉狀態和鑰匙狀態的和,mod 10000作為hash值插入相應數組的位置,然後判重時遍歷數組即可。
轉自本人的csdn博客: http://blog.csdn.net/xfww123/article/details/77131236
迷宮出逃
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 15 Accepted Submission(s): 1
不同於一般的迷宮,魔王在迷宮裏安置了機關,一旦觸碰,那麽四個方向所在的格子,將翻轉其可達性(原先可通過的格子不可通過,反之亦然,機關可以反復觸發)。為了防止小明很容易地出逃,魔王在臨走前把鑰匙丟在了迷宮某處,只有拿到鑰匙,小明才能開門在出口處離開迷宮。
萬般無奈之下,小明想借助聰明的你,幫忙計算是否有機會離開這個迷宮,最少需要多少時間。(每一單位時間只能向四鄰方向走一步)
下面 T 組數據,對於每組數據:
第一行是兩個數字 n, m(2 < n * m <= 64),表示迷宮的長與寬。
接下來 n 行,每行 m 個字符,‘.’表示空地可以通過,‘x’表示陷阱,‘*’表示機關,‘S’代表起點,‘E’代表出口,‘K’表示鑰匙(保證存在且只有一個)。
Case #i:
然後輸出一行,僅包含一個整數,表示最少多少步能夠拿到鑰匙並走出迷魂陣,如果不能則打出-1。
題目分析: M*N最大64,很容易就想到狀態壓縮,又因為是找到終點的最近距離,那麽肯定使用BFS。 狀態壓縮也很基礎,假設N行M列,從第一行第一列是二進制第一位,第一行第二列是二進制第二位,第一行第三列是二進制第三位 ,第二行第一列是二進制第M+1位,第x行第y列是二進制的第(x-1)*m+y位。每一位是1表示狀態反轉,每一位狀態是0表示狀態正常。 如此,就可以很輕松的,把狀態用一個64位的無符號整數儲存下來了。 然後有沒有鑰匙是2種狀態,0表示有,1表示沒有。 找到出口看鑰匙狀態是0還是1即可 每一位能不能走看那個位置原本的圖,和狀態是否改變即可。 那麽問題來了,bfs必須要判重,也就是要有一個哈希表,存儲這個狀態到底有沒有被訪問過。單純的反轉狀態就有2^64次方個,那麽,用一個數組去裸存肯定不行。比賽時,我就想到了map,結果超了內存。內存限制32MB,太小了。 然後評測機就一直卡著,然後比賽就這麽結束了。 其實,比賽完之後仔細想想,狀態絕對不會出現很多,因為圖太小了,我就寫了個hash表,建立了一個10000的queue數組,取每個狀態的坐標,反轉狀態和鑰匙狀態的和,mod 10000作為hash值插入相應數組的位置,然後判重時遍歷數組即可。
附代碼:
#include<iostream> using namespace std; #include<cstdio> #include<queue> #include<cstring> #include<vector> int tx[] = { -1,1,0,0 };//上下左右移動x的改變量 int ty[] = { 0,0,-1,1 };//上下左右移動y的改變量 int ii = 0; typedef unsigned long long LL; struct datatype//隊列中狀態信息 { int bs;//步數 LL zt1;//第一個狀態,儲存每個點的狀態是否反轉 LL zt2;//第二個狀態,儲存是否有鑰匙 int x;//所在的x坐標 int y;//所在的y坐標 datatype()//初始化 { bs = 0; zt1 = 0; x = 0; y = 0; zt2 = 0; } }; queue<datatype> team;//BFS所用的隊列 int maps[70][70];//初始的地圖圖信息 struct mapdata {//hash判重所用的信息類型 int x, y;//x,y坐標 LL zt1;//第一個狀態 int zt2;//第二個狀態 LL count()//每個狀態的和 mod 10000,取得hash值 { LL ans = zt1 % 10000; ans = (ans + zt2) % 10000; ans = (ans + x) % 10000; ans = (ans + y) % 10000; return ans; } bool operator<(const mapdata &b)const//重載 { if (this->x != b.x) return this->x< b.x; if (this->y != b.y) return this->y< b.y; if (this->zt1 != b.zt1) return this->zt1 < b.zt1; return this->zt2 < b.zt2; } bool operator==(const mapdata &b)const//重載 { if (this->x != b.x) return this->x == b.x; if (this->y != b.y) return this->y == b.y; if (this->zt1 != b.zt1) return this->zt1 == b.zt1; return this->zt2 == b.zt2; } }; char s[70][100];//最初讀入得字符串地圖信息 vector<mapdata> has[10010];//hash判重用到得vector數組 void deal() { memset(s, 0, sizeof(s));//初始化 memset(maps, 0, sizeof(maps));//初始化 int n, m; while (!team.empty()) team.pop();//清空隊列 datatype pt;//一個讀取隊列狀態的變量 cin >> n >> m;//讀取n,m,地圖長,寬 for (int i = 0; i <= 10000; i++) has[i].clear();//清空hash表 for (int i = 1; i <= n; i++) { scanf("%s", s[i] + 1);//讀取地圖信息 } int qdx, qdy;//保存起點x,y坐標 for (int i = 1; i <= n; i++)//處理地圖 { for (int j = 1; j <= m; j++) { if (s[i][j] == ‘.‘) maps[i][j] = 1;//可行為1 if (s[i][j] == ‘x‘) maps[i][j] = 0;//不可行為0 if (s[i][j] == ‘E‘) maps[i][j] = -666;//出口為-666 if (s[i][j] == ‘*‘) maps[i][j] = -10;//機關為-10 if (s[i][j] == ‘K‘) maps[i][j] = -66;//鑰匙為-66 if (s[i][j] == ‘S‘) {//找到起點,將起點入隊 maps[i][j] = 1, qdx = i, qdy = j; pt.bs = 0, pt.zt1 = 0, pt.zt2 = 0, pt.x = qdx, pt.y = qdy; team.push(pt); } } } datatype pd; int ans = -1;//初始化答案變量 while (!team.empty())//BFS { pt = team.front();//取隊首元素 team.pop();//隊首元素出隊 if (maps[pt.x][pt.y] == -666)//如果是出口,且有鑰匙,找到答案,退出搜索 { if (pt.zt2 == 1) { ans = pt.bs; break; } } for (int i = 0; i < 4; i++)//向當前位置的x,y軸移動 { pd.bs = pt.bs + 1;//更新步數 pd.zt1 = pt.zt1; pd.zt2 = pt.zt2; pd.x = pt.x + tx[i];//更新x坐標 pd.y = pt.y + ty[i];//更新y坐標 if (pd.x <= 0 || pd.y <= 0 || pd.x > n || pd.y > m) continue;//超出邊界,舍棄這個位置 LL cj = (LL)(m)*(pd.x - 1) + pd.y;//看當前坐標是地圖橫著數第幾個位置,也就是狀態壓縮後的第幾位,式子:(x-1)*m+y LL zt = ((pd.zt1 >> (LL)(cj - 1)) & (LL)1);//看當前坐標是否受機關影響 if (zt == 0 && maps[pd.x][pd.y] == 0) continue;//不受影響,原本不可行,舍棄這個位置 if (zt == 1) { if (maps[pd.x][pd.y] != 0) continue;//受機關影響,原本可行,舍棄這個位置 } LL ws1, ws2, ws3, ws4;//儲存上下左右四點 ,是地圖橫著數第幾個位置,也就是狀態壓縮後的第幾位 ws1 = 0; ws2 = 0; ws3 = 0; ws4 = 0; ws1 = (pd.x - 2)*(m)+pd.y; ws2 = (pd.x)*m + (pd.y); ws3 = (pd.x - 1)*m + (pd.y - 1); ws4 = (pd.x - 1)*m + (pd.y + 1); if (maps[pd.x][pd.y] == -10)//如果這個點是機關,就更新上下左右四點的反轉狀態 { if (pd.x - 1 > 0)//判斷向上走超不超過邊界 { if ((pd.zt1 >> (ws1 - (LL)1)) & (LL)1)//對應位置的翻轉狀態 pd.zt1 = pd.zt1 - ((LL)1 << (ws1 - (LL)1));//是1變成0 else pd.zt1 = pd.zt1 + ((LL)1 << (ws1 - (LL)1));//是0變成1 } /*後面的以此類推*/ if (pd.x + 1 <= n) { if ((pd.zt1 >> (ws2 - (LL)1)) & (LL)1) pd.zt1 = pd.zt1 - ((LL)1 << (ws2 - (LL)1)); else pd.zt1 = pd.zt1 + ((LL)1 << (ws2 - (LL)1)); } if (pd.y - 1 > 0) { if ((pd.zt1 >> (ws3 - (LL)1)) & (LL)1) pd.zt1 = pd.zt1 - ((LL)1 << (ws3 - (LL)1)); else pd.zt1 = pd.zt1 + ((LL)1 << (ws3 - (LL)1)); } if (pd.y + 1 <= m) { if ((pd.zt1 >> (ws4 - (LL)1)) & (LL)1) pd.zt1 = pd.zt1 - ((LL)1 << (ws4 - (LL)1)); else pd.zt1 = pd.zt1 + ((LL)1 << (ws4 - (LL)1)); } } if (maps[pd.x][pd.y] == -66)//該點存在鑰匙,鑰匙狀態設為1 { pd.zt2 = 1; } mapdata h;//建立一個hash信息的變量 h.x = pd.x; h.y = pd.y; h.zt1 = pd.zt1; h.zt2 = pd.zt2;//變量賦值 int zzt = h.count();//計算hash值 int vvv = 0;//判斷訪問與否的變量 for (int k = 0; k < has[zzt].size(); k++)//遍歷所在hash表中的節點 { if (has[zzt][k] == h)//該狀態已存在,標記已訪問 { vvv = 1; break; } } if (vvv) continue;//如果標記訪問,舍棄該狀態 has[zzt].push_back(h);//未訪問,hash信息入表 team.push(pd);//狀態信息入隊 } } /*輸出答案*/ printf("Case #%d:\n", ii); cout << ans << endl; } int main() { int t; cin >> t; while (t--) { ii++; deal(); } return 0; }
百度之星2017 初賽 迷宮出逃(HDU 6111)