SDUT oj 2193 救基友记3

一个简单的搜索题了

不过有钥匙开门什么的其他条件

(好吧这都不是重点


这题一般的做法应该是状态压缩

就是用一个小于1024的整数的二进制的位来表示当前持有的钥匙

然后检查当前位置的那个数组(我这里叫che

的第三维也开到1024

这样的话每个持有钥匙的状态都对应了这个数组的某一层

其他的就是位运算了

以下是代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int dir[4][2]={0,1,0,-1,1,0,-1,0};
bool che[25][25][1024];
char arr[25][25];
int key[10];
int dor[10];
int n,m,t;


struct state{
    int x;
    int y;
    int time;
    int tk;
    bool can(int k){
        x+=dir[k][0];
        y+=dir[k][1];
        time++;
        if(time>=t)
            return false;
        if(x<0 || x>=n || y<0 || y>=m || arr[x][y]=='*')
            return false;
        if(che[x][y][tk])
            return false;
        if('A'<=arr[x][y] && arr[x][y]<='J')
            if((tk|dor[arr[x][y]-'A'])!=key[10]){
                return false;
            }
        if('a'<=arr[x][y] && arr[x][y]<='j')
            tk=tk|key[arr[x][y]-'a'];

        che[x][y][tk]=true;
        return true;
    }
};

void fnd(state &p){
    for(p.x=0;p.x<n;p.x++)
        for(p.y=0;p.y<m;p.y++)
            if(arr[p.x][p.y]=='@')
                return;
}

int dfs(){
    state s;
    fnd(s);
    s.time=0;
    s.tk=0;
    queue<state> q;
    q.push(s);
    while(q.empty()==false){
        s=q.front();
        if(arr[s.x][s.y]=='^')
            return s.time;
        for(int i=0;i<4;i++){
            s=q.front();
            if(s.can(i))
                q.push(s);
        }
        q.pop();
    }
    return -1;
}

int main(){
    key[0]=1;
    for(int i=1;i<=10;i++)
        key[i]=key[i-1]*2;
    key[10]--;
    for(int i=0;i<10;i++)
        dor[i]=key[10]-key[i];
  //  #define DEBUG
    #ifdef DEBUG
        for(int i=0;i<=10;i++)
            printf(i<10?"%d ":"%d\n",key[i]);
        for(int i=0;i<=9;i++)
            printf(i<9?"%d ":"%d\n",dor[i]);
    #endif // DEBUG
    while(~scanf("%d %d %d",&n,&m,&t)){
        for(int i=0;i<n;i++)
            scanf("%s",arr[i]);
        memset(che,0,sizeof(che));
        printf("%d\n",dfs());
    }
    return 0;
}


其实吧,这不是我第一次的想法

刚开始就觉得没有必要开到1024这么多

优化过之后只要开到11就好了

(算了我还是直接说方法好了


这是从o(2^n)到o(n)的优化(虽然只是空间复杂度)


和上一份代码不同

这里的钥匙用一个bool数组表示

为真说明你带了这把钥匙

这里che数组的每一层都唯一对应着一个钥匙

那么che[x][y][k]为TRUE代表的含义是在(x,y)这个位置你带着第k把钥匙来过了

那么如果你身上的钥匙如果不增加的话,这个地方就不值得来了

大体就是这样

每次移动的时候检查这个che数组的每一层

如果你有对应的钥匙而且有你没去过的,那么可以走

不然你都带着钥匙来过了,就不用再来了

还有要注意的是刚出发的时候你没有钥匙

所以要另外设置一个虚拟钥匙(否则你刚出来就动不了了

(想一想,为什么

以上


-----------------------------------------------我是代码的分界线----------------------


#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int dir[4][2]={0,1,0,-1,1,0,-1,0};
bool che[25][25][11];
char arr[25][25];
int dor[10];
int n,m,t;


struct state{
    int x;
    int y;
    int time;
    bool key[11];
    bool can(int k){
        x+=dir[k][0];
        y+=dir[k][1];
        time++;
        if(time>=t)
            return false;
        if(x<0 || x>=n || y<0 || y>=m || arr[x][y]=='*')
            return false;
        bool flag=true;
        for(int i=0;i<=10;i++)
            flag=flag && !(key[i] && !che[x][y][i]);
        if(flag)
            return false;
        if('A'<=arr[x][y] && arr[x][y]<='J')
            if(!key[arr[x][y]-'A']){
                return false;
            }
        if('a'<=arr[x][y] && arr[x][y]<='j')
            key[arr[x][y]-'a']=true;
        for(int i=0;i<=10;i++)
            if(key[i])
                che[x][y][i]=true;
        return true;
    }
};

void fnd(state &p){
    for(p.x=0;p.x<n;p.x++)
        for(p.y=0;p.y<m;p.y++)
            if(arr[p.x][p.y]=='@')
                return;
}

int dfs(){
    state s;
    fnd(s);
    s.time=0;
    for(int i=0;i<=9;i++)
        s.key[i]=false;
    s.key[10]=true;
    queue<state> q;
    q.push(s);
    while(q.empty()==false){
        s=q.front();
        if(arr[s.x][s.y]=='^')
            return s.time;
        for(int i=0;i<4;i++){
            s=q.front();
            if(s.can(i))
                q.push(s);
        }
        q.pop();
    }
    return -1;
}

int main(){
    while(~scanf("%d %d %d",&n,&m,&t)){
        for(int i=0;i<n;i++)
            scanf("%s",arr[i]);
        memset(che,0,sizeof(che));
        printf("%d\n",dfs());
    }
    return 0;
}

-------------------------------------------------------------

其实这个优化是只是用时间换空间  (谢柴爷提醒

每次判断都加了一个o(s)  (s是钥匙的数量

的扫描



你可能感兴趣的:(SDUT oj 2193 救基友记3)