HDU 1044 [Collect More Jewels] DFS+BFS

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1044

题目大意:在地图中,有M个宝石,每个宝石有不同价值。问在时间限制L之内,从入口到出口这一路上获得的最大价值是多少。拿宝石不额外花时间,走一格用时为1.

关键思想:考虑到BFS和DFS的特点,BFS在解决最短(小)问题是很有效,但内存耗费巨大;DFS可以解决绝大多数搜索问题,但层数较深时,时间开销和栈的开销都很大。

这道题,只用DFS显然是不行的,地图比较大了。但是只用BFS也不行,因为取完之后地图状态会发生改变,考虑这样一种情况,如果终点邻近处有一个宝石,那当你时间多的时候,是会跨过终点取完宝石再回来的。只用一种搜索不行,那两种搜索并用吧。

我们先用BFS把起点、终点、各个宝石这些重要元素之间的最短用时解出来。(只有10多个)

然后用DFS搜索从起点经过重要元素到达终点的最大收益。好好想一想为什么可以这样算,你一定能理解。

代码如下:

#include <iostream>
#include <memory.h>
#include <stdio.h> 
#include <queue>
using namespace std;
const int INF=1e8;
const int MAXN=55;
const int MAXM=16; 
char m[MAXN][MAXN];
int dir[4][2]={0,1,1,0,0,-1,-1,0};

int W,H,L,M,sum,ans;//L时限

struct node{
    int x,y;
    int t;
};//[0]为起点,[T+1]为终点 
int v[MAXM];//0不用 

int newM[MAXM][MAXM];//构造出来的邻接矩阵 ,将重要元素作为点 

bool vis[MAXM];//DFS的时候用到 
bool vis2[MAXN][MAXN];//BFS的时候用到 ,这样不会爆空间 

void dfs(int p0,int money,int time){
    if(ans==sum||time>L)return;//sum这个优化很关键,TLE和AC的差别 
    if(p0==M+1){//如果到了终点,更新当前获得的钱 
        ans=max(ans,money);
    }
    for(int i=1;i<=M+1;i++){
        if(!vis[i]){
            vis[i]=true;
            dfs(i,money+v[i],time+newM[p0][i]);
            vis[i]=false;
        }
    }
    return;
}

void bfs(int x,int y,int from){//bfs计算重要元素间的最短时间 
    memset(vis2,0,sizeof(vis2)); 
    queue<node>q;
    node nw,nt;
    vis2[x][y]=true; 
    nw.x=x,nw.y=y,nw.t=0;
    q.push(nw);
    while(!q.empty()){
        nw=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            nt.x=nw.x+dir[i][0];
            nt.y=nw.y+dir[i][1];
            nt.t=nw.t+1;
            if(!vis2[nt.x][nt.y]&&nt.x>=1&&nt.x<=H&&nt.y>=1&&nt.y<=W&&m[nt.x][nt.y]!='*'&&nt.t<=L){//可以走的路径 
                if(m[nt.x][nt.y]<='Z'&&m[nt.x][nt.y]>='A'&&newM[from][m[nt.x][nt.y]-'A'+1]==INF){//如果找到重要的点就更新最短时间,加了一点优化 
                    newM[from][m[nt.x][nt.y]-'A'+1]=nt.t;
                    newM[m[nt.x][nt.y]-'A'+1][from]=nt.t;
                }
                else if(m[nt.x][nt.y]=='@'&&newM[from][0]==INF){
                    newM[from][0]=nt.t;
                    newM[0][from]=nt.t;
                }
                else if(m[nt.x][nt.y]=='<'&&newM[from][M+1]==INF){
                    newM[from][M+1]=nt.t;
                    newM[M+1][from]=nt.t;
                }
                vis2[nt.x][nt.y]=1;
                q.push(nt);
            }     
        }
    }
    return;
} 

int main(){
    int T;
    scanf("%d",&T);
    for(int cnt=1;cnt<=T;cnt++){
        memset(vis,0,sizeof(vis));
        memset(newM,-1,sizeof(newM));
        
        scanf("%d%d%d%d",&W,&H,&L,&M);
        
        ans=-1;
        sum=0;//sum保存宝石总价值 
        for(int i=1;i<=M;i++){scanf("%d",&v[i]);sum+=v[i];}
        v[M+1]=0; //要注意,DFS会涉及 ,终点没有宝石 
        for(int i=1;i<=H;i++){
            for(int j=1;j<=W;j++){
                scanf(" %c",&m[i][j]);
            }
        }
        //新图的邻接矩阵初始化 
        for(int i=0;i<=M+1;i++){
            for(int j=0;j<=M+1;j++){
                if(i!=j)newM[i][j]=INF;
                else newM[i][j]=0;
            }
        }
        
        //开始BFS获得重要元素间的最少耗时,要用到坐标i、j 
        for(int i=1;i<=H;i++){
            for(int j=1;j<=W;j++){
                if(m[i][j]<='Z'&&m[i][j]>='A'){
                    bfs(i,j,m[i][j]-'A'+1);
                }
                else if(m[i][j]=='@'){
                    bfs(i,j,0);
                }
                else if(m[i][j]=='<'){
                    bfs(i,j,M+1);
                }
            }
        }
        
        printf("Case %d:\n",cnt); 
        //newM[0][M+1]<=L
        if(newM[0][M+1]<=L){//如果起点到终点最短距离都大于L那肯定不行 
            dfs(0,0,0);
            printf("The best score is %d.\n",ans);
        }
        else printf("Impossible\n");
        if(cnt!=T)printf("\n");
    }
    return 0;
}

 

posted @ 2017-08-05 23:39  哇咔咔咔  阅读(241)  评论(0编辑  收藏  举报