hdu 1044 Collect More Jewels bfs+状态压缩/bfs+dfs

题意:

一个n*m迷宫,t时间后会倒塌,里边有l个珍宝。现在知道每个珍宝的价值,迷宫地图。问在能否逃出迷宫;若能逃出,能带走的珍宝最大价值是多少。

珍宝由大写字母【A】~【J】表示,‘*’表示墙,‘.’表示路,‘@’表示起点,‘<’表示出口。

题解:

两种方法:bfs+状态压缩或者bfs+dfs,前者只用写bfs,但耗时较高,734MS;后者则只需要31MS即可。

1)bfs+状态压缩

将珍宝状态压缩,对于l个珍宝,我们可以用l位二进制表示珍宝的获取情况(1表示已经获得,0表示未获得)。然后我们用vis[i][j][k]表示状态,i表示珍宝获取情况,(j,k)表示位置。这样我们相当于走2^l张地图,每次得到珍宝时转换地图,这样就需要相当多的时间消耗了。

2)bfs+dfs

珍宝至多10件,我们可以找出起点到所有珍宝和终点的最短路径,珍宝到其他珍宝和终点的最短路径。需要bfs地图至多21次,比上面的2^l次要少很多。我们得到所有的最短路径之后就可以用dfs搜索最大能获得价值了。

提示:可以用sum保存所有珍宝的价值和,要是dfs时,答案ans=sum,那么就可以剪枝掉了,why?因为没有比这个价值更大的了。

注意:我们需要把所有的最短路径path[i][j]初始化为无穷大,因为可能出现走不到的情况。


代码:

1.bfs+状态压缩

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
using namespace std;

//状态压缩+bfs
const int maxn=55;
struct node{
    int x,y;
    int key;//用10位二进制标识拿到了的珠宝
    int num;
};
char e[maxn][maxn];
int val[11];
bool vis[1100][maxn][maxn];
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};

int cal(int key)
{
    int i,j,ans=0;
    for(i=0,j=1;i<10;i++,j=j*2)
        if(j&key)ans+=val[i];
    return ans;
}
void bfs(int x1,int y1,int x2,int y2,int n,int m,int t)
{
    //printf("%d %d %d %d\n",x1,y1,x2,y2);
    memset(vis,false,sizeof(vis));
    node f,g;
    int i,j,k,xx,yy,key,ans=-1;
    f.x=x1,f.y=y1,f.key=f.num=0;
    vis[0][x1][y1]=true;
    queue<node>q;
    q.push(f);
    while(!q.empty())
    {
        f=q.front();
        q.pop();
        //printf("%d\n",f.num);
        for(i=0;i<4;i++)
        {
            g.x=f.x+dir[i][0];
            g.y=f.y+dir[i][1];
            if(g.x<0||g.y<0||g.x>=n||g.y>=m||e[g.x][g.y]=='*')continue;
            if(e[g.x][g.y]!='.')g.key=(f.key|(1<<(e[g.x][g.y]-'A')));
            else g.key=f.key;
            if(vis[g.key][g.x][g.y])continue;
            vis[g.key][g.x][g.y]=true;
            g.num=f.num+1;
            //printf("%d %d %d\n",g.x,g.y,g.key);
            if(g.x==x2&&g.y==y2)
                ans=max(ans,cal(g.key));
            if(g.num==t)continue;
            q.push(g);
        }
    }
    if(ans==-1)printf("Impossible\n");
    else printf("The best score is %d.\n",ans);
}
int main()
{
    int T,tt=0;
    scanf("%d",&T);
    while(T--)
    {
        int i,j,k,n,m,t,x1,y1,x2,y2;
        scanf("%d%d%d%d",&m,&n,&t,&k);
        for(i=0;i<k;i++)scanf("%d",&val[i]);
        for(i=0;i<n;i++)
            scanf("%s",e[i]);
        for(i=0;i<n;i++)
        {
            for(j=0;j<m;j++)
            {
                if(e[i][j]=='@'){x1=i;y1=j;e[i][j]='.';}
                else if(e[i][j]=='<'){x2=i;y2=j;e[i][j]='.';}
            }
        }
        printf("Case %d:\n",++tt);
        bfs(x1,y1,x2,y2,n,m,t);
        if(T!=0)printf("\n");
    }
    return 0;
}


2.bfs+dfs

#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn=55;
const int INF=1e8;
struct node{
    int x,y;
    int num;
};
int vis[maxn][maxn];
int n,m,t,l,ans,sum;
int val[11];
int path[12][12],mark[12];
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
char e[maxn][maxn];
void bfs(int x,int y,int from)
{
    memset(vis,0,sizeof(vis));
    vis[x][y]=1;
    int i,j,k;
    node f,g;
    queue<node>q;
    f.x=x;f.y=y;f.num=0;
    q.push(f);
    while(!q.empty())
    {
        f=q.front();
        q.pop();
        for(i=0;i<4;i++)
        {
            g.x=f.x+dir[i][0];
            g.y=f.y+dir[i][1];
            if(g.x<0||g.y<0||g.x>=n||g.y>=m||e[g.x][g.y]=='*'||vis[g.x][g.y])continue;
            vis[g.x][g.y]=1;
            g.num=f.num+1;
            if(e[g.x][g.y]!='.')
            {
                if(e[g.x][g.y]=='@')path[from][0]=g.num;
                else if(e[g.x][g.y]=='<')path[from][l+1]=g.num;
                else path[from][e[g.x][g.y]-'A'+1]=g.num;
            }
            q.push(g);
        }
    }
}
void dfs(int pre,int time,int value)
{
    if(time>t||ans==sum)return ;
    if(pre==l+1)
    {
        ans=max(ans,value);
        return ;
    }
    for(int i=1;i<=l+1;i++)
    {
        if(mark[i])continue;
        mark[i]=1;
        dfs(i,time+path[pre][i],value+val[i-1]);
        mark[i]=0;
    }
}
int main()
{
    int T,tt=0;
    scanf("%d",&T);
    while(T--)
    {
        int i,j,k;
        sum=0;
        scanf("%d%d%d%d",&m,&n,&t,&l);
        for(i=0;i<l;i++){scanf("%d",&val[i]);sum+=val[i];}
        val[l]=0;
        for(i=0;i<n;i++)scanf("%s",e[i]);
        for(i=0;i<=l+1;i++)
        {
            for(j=0;j<=l+1;j++)
                path[i][j]=INF;
        }
        for(i=0;i<n;i++)
        {
            for(j=0;j<m;j++)
            {
                if(e[i][j]=='@')bfs(i,j,0);
                if(e[i][j]=='<')bfs(i,j,l+1);
                if(e[i][j]<='J'&&e[i][j]>='A')bfs(i,j,e[i][j]-'A'+1);
            }
        }
        ans=-1;
        memset(mark,0,sizeof(mark));
        dfs(0,0,0);
        printf("Case %d:\n",++tt);
        if(ans==-1)printf("Impossible\n");
        else printf("The best score is %d.\n",ans);
        if(T!=0)printf("\n");
    }
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值