赞
踩
题目来源:杭电OJ-1044
题目大意:一个探险家身处一个危险的地下城,城中很危险,并且城中分散着若干个珠宝;现在地下城即将塌陷,冒险家需要在有限的时间内逃出去,但他希望在逃生的过程中获取一些珠宝并使价值最大化。题目要求用程序计算出最优方案所能获取的珠宝价值。
以前也遇到过相似的题目,但一直未真正搞明白,最近再次遇到,阅读了一些大牛的博客,结合自己的理解,将这个知识点记录一下。
这道题之所以不能简单的使用DFS或BFS算法解决,是因为探险家为了获取更多价值,可以走重复路径。最近学习并实现了这道题的两种解决思路,一种是BFS Plus,另一种是BFS+DFS。
一.BFS Plus方案
经典的BFS算法用一个二维数组保存地图的访问状况(可阅读我上一篇转载的文章,讲解的很深刻),而这一题的不同在于每次获取一个珠宝后,可以走重复路径,为了解决这个问题,我们可以在每次获取一个珠宝之后用一个新的二位数组保存后面遍历的访问情况。本方案使用二进制中的一位来标识某个珠宝是否获得,已获得取1,否则取0,这样就可以用一个值来表示珠宝的获取状态,比如说有四个珠宝A,B,C,D,那么我们可以用四位二进制数来表示获取状态,如果已经获取A和D,那么状态值就为9(二进制形式为1001)。本方案就是使用这个状态值来表示不同的二位访问状态数组,所以总体来说需要一个三维的数组来记录访问信息。而本题中最多有10个珠宝,所以需要十位二进制数来标识珠宝的访问状态,故状态数组的第一维为1024。
接下来给出该方案的源代码:
- #include <iostream>
- #include <queue>
- #include <algorithm>
- #include <string.h>
- using namespace std;
-
- int W, H, L, M;
- char dun[55][55]; //保存地下城地图
- int jew[10]; //保存每个珠宝的价值
- bool isVisited[1024][55][55]; //记录BFS过程中某个位置是否经过
- //结构体保存每到一个位置时的信息
- struct State
- {
- int J, x, y;
- int jews, time;
- };
- int dx[] = { -1, 0, 1, 0 };
- int dy[] = { 0, 1, 0, -1 };
-
- int CollectJewels()
- {
- memset(isVisited, 0, sizeof(isVisited));
- State begin, pos, nextpos;
- for (int i = 0; i < H; i++)
- {
- int j;
- for (j = 0; j < W; j++)
- if (dun[i][j] == '@')
- {
- begin.x = i;
- begin.y = j;
- break;
- }
- if (j != W) break;
- }
- begin.J = begin.jews = begin.time = 0;
- isVisited[0][begin.x][begin.y] = true;
- queue<State> Q;
- Q.push(begin);
- int res = -1;
- while (!Q.empty())
- {
- pos = Q.front();
- Q.pop();
- if (pos.time == L) break; //优化操作,如果当前位置的时间已经达到
- //L,那么队列后面位置的时间一定>=L,可终止遍历
- int i;
- for (i = 0; i < 4; i++)
- {
- nextpos = pos;
- nextpos.x += dx[i];
- nextpos.y += dy[i];
- nextpos.time++;
- if(nextpos.x<0 || nextpos.x>=H ||
- nextpos.y<0 || nextpos.y>=W) continue;
- char ch = dun[nextpos.x][nextpos.y];
- if (ch == '*' || isVisited[nextpos.J][nextpos.x][nextpos.y])
- continue;
- isVisited[nextpos.J][nextpos.x][nextpos.y] = true;
- if (ch >= 'A' && ch <= 'J')
- {
- int t = ch - 'A';
- //如果当前位置的珠宝还在,取之
- if (!(nextpos.J & (1 << t)))
- {
- nextpos.jews += jew[t];
- nextpos.J |= (1 << t);
- isVisited[nextpos.J][nextpos.x][nextpos.y] = true;
- }
- }
- else if (ch == '<')
- {
- res = max(res, nextpos.jews);
- //优化操作,如果某种方案可以取得城内所有珠宝,则可以停止
- //尝试其他方案
- if (nextpos.J == ((1 << M) - 1)) break;
- else continue;
- }
- Q.push(nextpos);
- }
- if(i!=4) break;
- }
- return res;
- }
-
- int main() {
- int T;
- cin >> T;
- for (int i = 1; i <= T; i++)
- {
- cin >> W >> H >> L >> M;
- for (int j = 0; j < M; j++)
- cin >> jew[j];
- for (int j = 0; j < H; j++)
- cin >> dun[j];
- int res = CollectJewels();
- cout << "Case " << i << ":\n";
- if (res == -1) cout << "Impossible\n";
- else cout << "The best score is " << res << ".\n";
- if (i != T) cout << endl;
- }
-
- return 0;
- }
- #include <iostream>
- #include <queue>
- #include <string.h>
- using namespace std;
-
- int W, H, L, M, ans, sum;
- char dun[55][55]; //保存地下城地图信息
- int jew[15]; //保存每个珠宝的价值
- bool isVisited[55][55]; //记录BFS过程中地图的访问状态
- int Graph[15][15]; //保存BFS过程中构造的虚拟图的信息
- bool isUsed[15]; //记录DFS过程中图中节点的访问状态
-
- struct State
- {
- int x, y;
- int time;
- State(int a, int b, int c) :x(a), y(b), time(c){}
- };
- int dx[] = { -1, 0, 1, 0 };
- int dy[] = { 0, 1, 0, -1 };
-
- void BFS(int x, int y)
- {
- memset(isVisited, 0, sizeof(isVisited));
- char ch = dun[x][y];
- int u;
- if(ch=='@') u=0;
- else if (ch >= 'A') u = ch - 'A' + 1;
- State pos(0, 0, 0), nextpos(0, 0, 0);
- queue<State> Q;
- isVisited[x][y] = true;
- Q.push(State(x, y, 0));
- while (!Q.empty())
- {
- pos = Q.front();
- Q.pop();
- if (pos.time == L) break; //优化操作
- for (int i = 0; i < 4; i++)
- {
- nextpos.x = pos.x + dx[i];
- nextpos.y = pos.y + dy[i];
- nextpos.time = pos.time + 1;
- if (nextpos.x < 0 || nextpos.x >= H ||
- nextpos.y < 0 || nextpos.y >= W) continue;
- if (dun[nextpos.x][nextpos.y] == '*' || isVisited[nextpos.x][nextpos.y])
- continue;
- isVisited[nextpos.x][nextpos.y] = true;
- char ch = dun[nextpos.x][nextpos.y];
- if (ch == '@')
- {
- Graph[u][0] = Graph[0][u] = nextpos.time;
- }
- else if (ch >= 'A' && ch <= 'J')
- {
- int v = ch - 'A' + 1;
- Graph[u][v] = Graph[v][u] = nextpos.time;
- }
- else if (ch == '<')
- {
- Graph[u][M + 1] = Graph[M + 1][u] = nextpos.time;
- }
- Q.push(nextpos);
- }
- }
- }
-
- void DFS(int pos, int Time, int je)
- {
- if (Time > L) return;
- if(ans==sum) return; //剪枝操作
- if (pos == M + 1)
- {
- if (je > ans) ans = je;
- return;
- }
-
- for (int i = 1; i < M + 2; i++)
- {
- if (isUsed[i] || Graph[pos][i] == 0) continue;
-
- isUsed[i] = true;
- DFS(i, Time + Graph[pos][i], je + jew[i]);
- isUsed[i] = false;
- }
- }
- int main()
- {
- int T;
- cin >> T;
- for (int i = 1; i <= T; i++)
- {
- cin >> W >> H >> L >> M;
- sum=0;
- for (int j = 1; j <= M; j++)
- {
- cin >> jew[j];
- sum+=jew[j];
- }
- //把起始位置和出口记作价值为0的位置,分别用0和M+1标识
- jew[0]=jew[M+1]=0;
- for (int i = 0; i < H; i++)
- cin >> dun[i];
- memset(Graph,0,sizeof(Graph));
- //BFS过程中构造虚图
- for (int i = 0; i < H;i++)
- for (int j = 0; j < W; j++)
- {
- if (dun[i][j] == '@' || (dun[i][j] >= 'A' && dun[i][j] <= 'J'))
- BFS(i, j);
- }
- memset(isUsed, 0, sizeof(isUsed));
- isUsed[0]=true;
- ans = -1;
- //DFS在途中寻找一条最优路径
- DFS(0, 0, 0);
- cout<<"Case "<<i<<":\n";
- if (ans == -1) cout << "Impossible\n";
- else cout << "The best score is " << ans << ".\n";
- if (i != T) cout << endl;
-
- }
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。