广搜状态压缩 OpenJudge 百炼4105

拯救公主
总时间限制:
1000ms
内存限制:
65536kB

描述

多灾多难的公主又被大魔王抓走啦!国王派遣了第一勇士阿福去拯救她。

身为超级厉害的术士,同时也是阿福的好伙伴,你决定祝他一臂之力。你为阿福提供了一张大魔王根据地的地图,上面标记了阿福和公主所在的位置,以及一些不能够踏入的禁区。你还贴心地为阿福制造了一些传送门,通过一个传送门可以瞬间转移到任意一个传送门,当然阿福也可以选择不通过传送门瞬移。传送门的位置也被标记在了地图上。此外,你还查探到公主所在的地方被设下了结界,需要集齐K种宝石才能打开。当然,你在地图上也标记出了不同宝石所在的位置。

你希望阿福能够带着公主早日凯旋。于是在阿福出发之前,你还需要为阿福计算出他最快救出公主的时间。

地图用一个R×C的字符矩阵来表示。字符S表示阿福所在的位置,字符E表示公主所在的位置,字符#表示不能踏入的禁区,字符$表示传送门,字符.表示该位置安全,数字字符0至4表示了宝石的类型。阿福每次可以从当前的位置走到他上下左右四个方向上的任意一个位置,但不能走出地图边界。阿福每走一步需要花费1个单位时间,从一个传送门到达另一个传送门不需要花费时间。当阿福走到宝石所在的位置时,就视为得到了该宝石,不需要花费额外时间。

输入
第一行是一个正整数T(1 <= T <= 10),表示一共有T组数据。
每一组数据的第一行包含了三个用空格分开的正整数R、C(2 <= R, C <= 200)和K,表示地图是一个R×C的矩阵,而阿福需要集齐K种宝石才能够打开拘禁公主的结界。
接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。$的数量不超过10个。宝石的类型在数字0至4范围内,即不会超过5种宝石。
输出
对于每一组数据,输出阿福救出公主所花费的最少单位时间。若阿福无法救出公主,则输出“oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。
样例输入
1
7 8 2
……..
..S..#0.
.##..1..
.0#…..
…1#…
…##E..
…1….
样例输出
11

拿这组测试数据分析

7 8 2
……..
..S..#0.
.#$$..1.
.0#…$.
…1#…
..$…..
..E…..

这是一个状态压缩问题,尽量把自己的思路写详细一点。最主要的部分就是用一个三维数组dp[tx][ty][tk]代表在阿福(tx,ty)这个点上面拿了几个宝石tk。例如一开始阿福(2,3)这个点上面没有宝石所以状态为dp[2][3][0],但是阿福要前往下一个点,需要判重防止下次到达旁边的点后又走回来,所以将dp[2][3][0]赋值为1,…….当到达(4,7)时阿福所拥有的宝石仍为0,所以dp[4][7][0]=1,当到达(3,7)这个点时dp[3][7][2]=1, 为什么是2呢?因为宝石的信息是不一样的,是0号宝石,还是1号宝石,还是2号宝石……!所以用二进制编码的1111代表拥有0,1,2,3四块宝石,用1011代表拥有了0,1,3号宝石!然后将其转换成10进制数字,这样就记录了阿福在每次移动时所携带宝石具体信息(到底是哪几块)。在移动到(2,7)位置时状态转化为dp[2][7][3], 下次再移动到(3,7)这个位置时其状态为dp[3][7][3],所以在两次到达(3,7)的位置时其状态是不一样的<所携带的宝石是不一样的>。在具体实现宝石的获取和判断时,用 | 运算。因为需要集齐k个宝石(假如给了0,1,2,3 四种宝石但是输入的k值为2,所以加了一个check()加以判断)。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<limits>
using namespace std;
#define MAX  201
#define  N   1<<5-1   //不减1  是100000  减之后就是11111--代表5种宝石全都拥有的情况
#define  CS   10
char maps[MAX][MAX];
int dp[MAX][MAX][N];
int R,C,K;
int cuansong[CS][2];  //保存传送门的坐标
int numofcs=0;//传送门的数量
int dir[2][4]={{1,0,0,-1},{0,1,-1,0}};
int dignum=0; //整个地图宝石总数,当需要搜集的种类大于它时,是个根本不可能完成的任务!
struct node{
    int x;
    int y;
    int step;
    int key;
    node(int xx,int yy,int ss,int kk):x(xx),y(yy),step(ss),key(kk){};
};
bool check(int tk){
    int cnt=0;
    for(int i=0;i<=5;i++){
        if((tk>>i)&1)cnt++;
    }
    if(cnt>=K) return true;
    return false;
}
int BFS(int x,int y){
    queue<node> que;
    dp[x][y][0]=1;
    node temp(x,y,0,0);
    que.push(temp);
    while(!que.empty()){
        temp=que.front();
        que.pop();
        for(int i=0;i<4;i++){     //上下左右四方向
            int tx=temp.x+dir[0][i];
            int ty=temp.y+dir[1][i];
            int tt=temp.step;
            int tk=temp.key;
            if(dp[tx][ty][tk]) continue;
            if(tx<0 || tx>=R  || ty<0  || ty>=C ) continue;
            if(maps[tx][ty]=='#') continue;
            if(maps[tx][ty]=='E'){
                if(check(tk)) return temp.step+1;
            }
            if(isdigit(maps[tx][ty])){    //是数字的情况
                dp[tx][ty][tk]=1;
                tk=tk|(1<<(maps[tx][ty]-'0'));  //  | 运算
                que.push(node(tx,ty,tt+1,tk));
            }
            if(maps[tx][ty]=='.'){
                dp[tx][ty][tk]=1;
                que.push(node(tx,ty,tt+1,tk));
            }
            if(maps[tx][ty]=='$'){
                dp[tx][ty][tk]=1;
                que.push(node(tx,ty,tt+1,tk));
                for(int j=0;j<numofcs;j++){  //将其分布到每个传送门去,且不花费时间
                    tx=cuansong[j][0];
                    ty=cuansong[j][1];
                    if(!dp[tx][ty][tk]){
                        dp[tx][ty][tk]=1;
                        que.push(node(tx,ty,tt+1,tk));
                    }
                }
            }
        }
    }
    return -1;
}
int main(){
    int n;
    cin>>n;
    int x,y;
    while(n--){
        numofcs=0;
        dignum=0;
        memset(dp,0,sizeof(dp));
        cin>>R>>C>>K;
        for(int i=0;i<R;i++)
        for(int j=0;j<C;j++){
            cin>>maps[i][j];
            if(maps[i][j]=='S'){
                x=i;
                y=j;
                maps[i][j]='.';
            }
            if(maps[i][j]=='$'){
                cuansong[numofcs][0]=i;
                cuansong[numofcs][1]=j;
                numofcs++;
            }
            if(isdigit(maps[i][j]))
                dignum++;

        }
        if(K>dignum){
            cout<<"oop!"<<endl;
            continue;
        }
        int ans=BFS(x,y);
        if(ans==-1) cout<<"oop!"<<endl;
        else cout<<ans<<endl;
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值