题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1044
题目大意:迷宫用一个二维数组表示,'@'表示起点,'*'表示障碍物不能到达,'.'表示路径,'<'表示终点,’A'~'J'表示宝石,宝石的价值之前输入。每一秒只能上下左右移动一步。求在规定时间限制内可以走出迷宫,拿到的最大的宝石价值。注意这道题每个位置是可以反复通过的。
我一开始的思路是纯粹的DFS求解,每个位置最多到达3次,设定一个数组记录到达各个位置的次数。如果当前时间超过规定时间以后return。由于限制时间很大,并且所有位置可以反复经过,所以会有大量的DFS,一定会超时。
后来看了一些解答以后大致了解了一下解题思路:
例如我们一共有2个宝石,标号为1,2。将起点标号为0,将终点标号为3。我们首先求出任意两个元素彼此之间的最短路径:
例如shortest_path[0][2]表示从起点0到终点2的最短路径。
这样可以用DFS求解(任意一步都是二者之间的最短路径):
0->1->2->3,0->1->3,0->2->1->3这样一步一步的DFS求解收集宝石最大价值,如果期间超过了限定时间,则返回即可。这里每一步DFS深入都是二者之间的最短路径,所以不用考虑每个位置可以重复经过的问题。
那么任意两个元素之间的最短路径怎么求解?用BFS求解就好。
例如这样的一个迷宫:
@.A.B
**<**
首先以@为起点,记step[i][j]为从起点到达(i,j)的最短路径。初始step[0][0]=0
邻近的(0,1)点入队列。step[0][1]=step[0][0]+1=1
队首元素出队列,队首元素邻近的(0,2)元素入队列,step[0][2]=step[0][1]+1=2,shortest[0][2]=2
队首元素(0,2)出队列,邻近的(1,2)入队列step[1][2]=step[0][2]+1=3,shortest[1][2]=3,邻近的(0,3)入队列step[0][3]=step[0][2]+1=3。
队首元素(1,2)出队列,无邻近元素
队首(0,3)出队列,邻近元素(0,4)入队列,step[0][4]=step[0][3]+1=4。
队首元素出队列,无邻近元素。队列为空,结束。
同样的以A为起点可以求出A到其他元素的最短路径。
我的求解:
#include<cstdio>
#include<iostream>
#include<deque>
using namespace std;
/*
一共M个宝石,编号从1-M
起点为0,终点为M+1
*/
const int direct[4][2]={
{-1,0},{1,0},{0,-1},{0,1}
};
void bfs(int i,int j,int index);
void dfs(int index,int time,int total_value);
bool accessable(int x,int y);
char point[55][55];
int shortest_path[15][15];//shortest_path[i][j]:i到j的最短路径长度
bool visited[55][55];//标识是否到达过
int dfs_times[15];//dfs过程中,i到达的次数
int T;
int W,H,L,M;//W:长、H:宽、L:时间限制、M:宝石数量
int jewels[15];
int max_value=-1;//规定时间内能够拿到的最大价值
int sum_value=0;//所有宝石的总价值
int main(){
scanf("%d",&T);
int _case=1;
for(_case=1;_case<=T;_case++){
scanf("%d%d%d%d",&W,&H,&L,&M);
sum_value=0;
for(int i=0;i<M;i++) {
scanf("%d",&jewels[i+1]);//jewels[1]~jewels[M]是宝石
sum_value+=jewels[i+1];
}
for(int i=0;i<H;i++){
getchar();
for(int j=0;j<W;j++){
scanf("%c",&point[i][j]);
}
}
jewels[0]=jewels[M+1]=0;
//BFS求起点到宝石、终点、宝石到终点的最短路径
for(int i=0;i<=M+1;i++){
for(int j=0;j<=M+1;j++){
shortest_path[i][j]=-1;//小于0表示不可达
}
}
for(int i=0;i<H;i++){
for(int j=0;j<W;j++){
if(point[i][j]=='@'){
//求起点到宝石、终点的最短路径
bfs(i,j,0);
}else if(point[i][j]>='A'&&point[i][j]<='J'){
bfs(i,j,point[i][j]-'A'+1);
}
}
}
/*
cout<<"bfs result:"<<endl;
for(int i=0;i<=M+1;i++){
for(int j=0;j<=M+1;j++){
cout<<shortest_path[i][j]<<" ";
}
cout<<endl;
}
cout<<endl;
*/
//dfs求解整个问题
for(int i=0;i<=M+1;i++) dfs_times[i]=0;
max_value=-1;
dfs(0,0,0);
printf("Case %d:\n",_case);
if(max_value!=-1)
printf("The best score is %d.\n",max_value);
else
printf("Impossible\n");
if(_case!=T) printf("\n");
}
}
void bfs(int x,int y,int index){
//求index到其他地点的最短距离
//cout<<"bfs x="<<x<<" y="<<y<<" index="<<index<<endl;
deque<int> q;
int step[55][55];//step[m][n]:point[i][j]到point[m][n]的最短距离
for(int i=0;i<H;i++){
for(int j=0;j<W;j++){
visited[i][j]=false;
}
}
q.clear();
q.push_back(x*W+y);
step[x][y]=0;
visited[x][y]=true;
shortest_path[index][index]=0;
while(!q.empty()){
int p=q.front();
int current_x=p/W;
int current_y=p%W;
//cout<<"current_x="<<current_x<<" current_y="<<current_y<<endl;
q.pop_front();
for(int i=0;i<4;i++){
int next_x=current_x+direct[i][0],next_y=current_y+direct[i][1];
if(accessable(next_x,next_y)){
//cout<<"next_x="<<next_x<<" next_y="<<next_y<<endl<<endl;
visited[next_x][next_y]=true;
step[next_x][next_y]=step[current_x][current_y]+1;
if(point[next_x][next_y]=='<') shortest_path[index][M+1]=step[next_x][next_y];
else if(point[next_x][next_y]>='A'&& point[next_x][next_y]<='J'){
int target=point[next_x][next_y]-'A'+1;
shortest_path[index][target]=step[next_x][next_y];
}
else if(point[next_x][next_y]=='@')
shortest_path[index][0]=step[next_x][next_y];
int _next=next_x*W+next_y;
q.push_back(_next);
}else
continue;
}
}
}
//dfs当前在index,当前时间是time,当前获得的价值为total_value
void dfs(int index,int time,int total_value){
//cout<<"dfs index="<<index<<" time="<<time<<" total_value="<<total_value<<endl;
if(max_value==sum_value) {
return;
}
if(time>L) return;
dfs_times[index]++;
if(index==M+1){
//到终点了
if(total_value>max_value) max_value=total_value;
dfs_times[index]--;
return;
}
//从index->i
for(int i=0;i<=M+1;i++){
if(i==index) continue;
else{
if(dfs_times[i]==0 && shortest_path[index][i]>0){
dfs(i,time+shortest_path[index][i],total_value+jewels[i]);
if(max_value==sum_value) {
return;
}
}
}
}
dfs_times[index]--;
return;
}
bool accessable(int x,int y){
if( x<0 || x>=H || y<0 || y>=W || visited[x][y] || point[x][y]=='*' ){
return false;
}
else
return true;
}