hdoj1044 Collect More Jewels BFS求最短路径+DFS求解

题目链接: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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值