【 OJ 】 HDOJ1026 18年11月10日22:22 [ 25 ]
這題 Memory Limit Exceeded Memory Limit:32768 K本題在測試機上是35343K
這題如果想AC可以不用看本人的話了....因為超了記憶體,如果只是想了解思想,強烈推薦,感覺還是很不錯滴~自賣自誇下
這題我用了visit 來記錄了所有能到達結點的最短路徑數(並不需要全做,只要到了目的地就可以retun結束)【想了一下這句話也是錯了,原因很簡單,雖然正常來說在佇列中先被訪問的一定是比較近的點,但是由於此題有n怪物,所以不一定是最近的,有可能目的地確實先被訪問了,但它其實可以被鬆弛操作,意思它途徑了n怪物點,因此它不是最近的路徑,此時return了答案可能會不對】
找路徑,又想通過標記方向陣列 -> 或者 <- 來找路徑 後來用數字模擬方向
用dri 記錄所有點到達map[0][0]的路徑,逆推 1 ( -> )東 2 ( v )南 3 ( <- ) 西 4 ( ^ )北
重理思路:
之前說不需要做最短路徑數是不對的,後來想了想,如果沒有最短路徑數那麼就沒辦法確定是否做鬆弛操作,沒法確定是否做鬆弛操作就沒法確定是否修改指向.... 因此此題超記憶體.....當遇到目的地後直接return 剩下點都不用計算了,可能會....節省點記憶體
1: 首先思想BFS用佇列實現
2 : 路徑的最後輸出又棧實現【可由陣列代替】
從(0,0)開始入隊,讀取到一個當前節點 p 就遍歷他的四周所有可行點,如果【第一次發現這個節點】,將這個節點入隊,然後標記路徑長,為當前 p 節點路徑+1,如果是 n,
那麼就再 +n (注 * 前面+1必做),如果遍歷發現這個節點【不第一次發現這個節點】,那麼就看能否鬆弛【鬆弛操作】,就是當前 p 節點+1是否小於這個節點目前的路徑數,如果小於,那麼這個節點還需要入佇列,並且修改最短路徑
如果不小於說明不用做 ---------以上BFS可以求出所有可到達節點的最短路徑長度
(其實通過所有最短路徑數也是可以逆推出路徑的,從目的結點開始每次少1步就是,遇到n,減去(n+1)即可)
因為路徑數為6的結點周圍可能3個都是路徑數為7的,但是7的周圍肯定只有1個,因此可以逆推,但是不方便,
所以加上指向肯定會更加方便找
因此本題思路來源於 BFS 兩個串的最長共同子序列 (在陣列中可以標記最短路徑長度也可以給出路徑的方向)如果有疑問可以看 演算法導論的 BFS 例題講解
下面的所有操作其實是加在上述的求最短路徑數步驟中的,讀者自行體會
依舊從(0,0)開始入隊遍歷周圍每一個可行結點,如果第一次發現,將那個結點 標記下方向
【例如 當前點 p為 0,0發現向東走 0,1可以走,那麼0,1點就標記為3(西)說明(0,1)向西走可以回到當前結點-這樣從目的結點可以通過指向一路走到起始位置】
如果不是第一次,還是看是否能鬆弛,如果能鬆弛就修改指向
直至整個地圖遍歷結束
//下面的函式用來輸出BFS後地圖的所有能到達點的最短路徑
for (i = 0; i < N; i++) {
for (j = 0; j < M; ++j) {
if (visit[i][j] == IFMAX)
cout << "X ";
else
cout << visit[i][j]<<" ";
}
cout << endl;
}//鬆弛後所有的地圖資訊已經清晰
//下面的函式用來輸出地圖的方向 1東 2 南 3西 4北 從 map[N-1][M-1]看方向到map[0][0]
for (i = 0; i < N; i++) {
for (j = 0; j < M; ++j) {
if (i == 0 && j == 0)
cout << "S";
else {
if (dri[i][j] == 0)
cout << "X";
else
cout << dri[i][j];
}
}
cout << endl;
}
//下面是原始碼
# include <iostream>
# include <queue>
# include <stack>
#define IFMAX 200
using namespace std;
int N, M;//行 列
struct Path {
int x;
int y;
int p;
}p;
int visit[102][102];//題意 最大100*100
int dri[102][102];
char map[102][102];
int d[4][2] = { { 0,1 },{ 1, 0 },{ 0, -1 },{ -1, 0 } };
int min(int a, int b) {
return a < b ? a : b;
}
void PUSH(int dir_, queue<Path>&q) {
if (map[p.x][p.y] == '.') {//正常步子
if (p.p + 1<visit[p.x][p.y]) {//發現能被鬆弛就入棧
visit[p.x][p.y] = ++p.p;
q.push(p);
dri[p.x][p.y] = dir_;
}
}
else {//為n
if (visit[p.x][p.y] == IFMAX) {
q.push(p);//第一次訪問入隊
dri[p.x][p.y] = dir_;
}
visit[p.x][p.y] = min((p.p + 1 + (map[p.x][p.y] - '0')), visit[p.x][p.y]);//鬆弛操作是不是第一次入隊都要做
if ((p.p + 1 + (map[p.x][p.y] - '0')) < visit[p.x][p.y]) {//可以被鬆弛
q.push(p);//可以鬆弛入隊
dri[p.x][p.y] = dir_;
}
}
}
void check(queue<Path>&q) {
//地圖資訊 X . n
for (int i = 1; i < 5; ++i) {//東 南 西 北
p.x += d[i-1][0];
p.y += d[i-1][1];
if ((p.x >= 0 && p.x < N) && (0 <= p.y&&p.y < M) && map[p.x][p.y] != 'X') {//東
PUSH((((i+1)%4)+1), q);
}
p = q.front();//回溯原來位置
}
}
void BFS(void) {
queue<Path> q;
p.x = p.y = p.p = 0;
q.push(p);
while (!q.empty()) {
p = q.front();
p.p = visit[p.x][p.y];
check(q);//檢查四周,將可入隊的入隊
q.pop();//將遍歷過的出隊
}//遍歷完所有地圖
}
void TravelStack(stack<Path> &s) {
int n, num = 0;
Path pi;
p = s.top();
s.pop();
while (!s.empty()) {
pi = p;
if (map[pi.x][pi.y] != '.') {
n = map[pi.x][pi.y] - '0';
for (int i = 0; i < n; ++i) {
cout << ++num << "s:FIGHT AT(" << pi.x << ", " << pi.y << ")" << endl;
}
}
p = s.top();
s.pop();
cout << ++num << "s:(" << pi.x << ", " << pi.y << ")->(" << p.x << ", " << p.y << ")" << endl;
}
cout << "FINISH" << endl;
}
void TrevealPath(void) {
p.x = N - 1;
p.y = M - 1;//終點標記起點位置
p.p = visit[N - 1][M - 1];
if (p.p == IFMAX) {//暗示了路徑是不通的,所以呼叫上帝
cout << "God please help our poor hero.\nFINISH" << endl;
return;
}
stack<Path> s;
s.push(p);//起始點入棧
while (p.x != 0 || p.y != 0) {//終止位置不為(0,0)就迴圈
for (int i = 1; i < 5; ++i) {
if (dri[p.x][p.y] == i) {
p.x += d[i - 1][0];
p.y += d[i - 1][1];
s.push(p);
break;
}
}
}
cout << "It takes " << visit[N - 1][M - 1] << " seconds to reach the target position, let me show you the way." << endl;
TravelStack(s);
}
int main(void) {
while (cin >> N >> M) {// N 行 M 列
int i, j;
for (i = 0; i < N; i++) {
for (j = 0; j < M; ++j) {
cin >> map[i][j];
visit[i][j] = IFMAX;
dri[i][j] = 0;
}
}//錄入地圖資訊
visit[0][0] = 0;
BFS();//BFS map資訊
TrevealPath();
}
system("pause");
return 0;
}