高级搜索题集


题集转自以下链接:

https://blog.csdn.net/shahdza/article/details/7986044


 胜利大逃亡(续) HDU - 1429 

题意:就是二维地图,有障碍;重点有门,和钥匙(用于开门)。

理解:因为找钥匙,这个解答路径可能会走回头路。

思路:第一遍,因为有时间限制,并且20*20;所以简单bfs,然后MLimit了。感觉就是重复存储点在队列。

那么难点就在于判重了。因为走回头路,简单二维判重肯定不行。

回想和之前炮台那题,因为子弹的原因也是走回头路,所以判重数组多加了一个时间维度。

这题关键因素是钥匙,所以加一个钥匙拥有状态的判重,就ac了。(用2进制状态压缩,这里因为a-j,所以数组开个1024就够了。)

#include 
#include 
#include 
#include 

using namespace std;

int n,m,t;
char ditu[21][21];
bool vis[1025][21][21];
int Sx,Sy,Fx,Fy;

int xx[] = {0,0,1,-1};
int yy[] = {1,-1,0,0};

struct node{
    int x,y,step,k=0;
    node(int a,int b,int c,int d):x(a),y(b),step(c),k(d) {};
};


bool bfs(){
    memset(vis,false,sizeof vis);
    queue q;q.push(node(Sx,Sy,0,0));vis[0][Sx][Sy] = true;
    while(!q.empty()){
        node p = q.front();q.pop();
        for(int i=0;i<4;i++){
            int x = p.x+xx[i],y = p.y+yy[i];
            if(x<0||x>=n||y<0||y>=m||ditu[x][y]=='*'||p.step+1>=t) continue;
            int step = p.step + 1,k = p.k;
            if(ditu[x][y]>='A'&&ditu[x][y]<='Z'&&!(p.k&(1<'A'))) continue;
            if(ditu[x][y] == '^') {cout<return true;}
            if(ditu[x][y]>='a'&&ditu[x][y]<='z') k |= 1<<(ditu[x][y]-'a');
            if(vis[k][x][y]) continue;
            q.push(node(x,y,step,k));vis[k][x][y] = true;
        }
    }
    return false;
}

int main()
{
    while(cin>>n>>m>>t){
        for(int i=0;ifor(int j=0;j){
            char ch = getchar();while(ch==' '||ch=='\n') ch = getchar();
            ditu[i][j] = ch;
            if(ch=='^') Fx = i,Fy = j;
            else if(ch=='@') Sx = i,Sy = j;
        }

        if(!bfs()) puts("-1");
    }
    return 0;
}

 这题给我启发就是,判重作用在于剪枝,避免重复的入队操作。那么关键在于什么?关键在于点状态更新,并且取决于什么要素关键,炮台那题,因为子弹原因,时间是关键。这题因为门的原因,钥匙是关键。所以添加关键要素的判重数组是解题关键。



Key Task HDU - 1885 

题意与上题一样。但是地图大小和钥匙,门出口数量不同。

#include 
#include 
#include 
#include 
#include 

using namespace std;

int n,m;
char ditu[101][101];
bool vis[17][101][101];
int Sx,Sy,Fx,Fy;

int xx[] = {0,0,1,-1};
int yy[] = {1,-1,0,0};

struct node{
    int x,y,step,k=0;
    node(int a,int b,int c,int d):x(a),y(b),step(c),k(d) {};
};

int mp[5];

bool bfs(){
    memset(vis,false,sizeof vis);
    queue q;q.push(node(Sx,Sy,0,0));vis[0][Sx][Sy] = true;
    while(!q.empty()){
        node p = q.front();q.pop();
        for(int i=0;i<4;i++){
            int x = p.x+xx[i],y = p.y+yy[i];
            if(x<0||x>=n||y<0||y>=m||ditu[x][y]=='#') continue;
            int step = p.step + 1,k = p.k;
            if(ditu[x][y] == 'X') {cout<<"Escape possible in "<" steps."<return true;}
            if(ditu[x][y]>='A'&&ditu[x][y]<='Z'&&!(p.k&(1<'A']))) continue;
            if(ditu[x][y]>='a'&&ditu[x][y]<='z') k |= 1<'a'];
            if(vis[k][x][y]) continue;
            q.push(node(x,y,step,k));vis[k][x][y] = true;
        }
    }
    return false;
}

int main()
{
    mp['B'-'A'] = 0,mp['Y'-'A'] = 1,mp['R'-'A'] = 2,mp['G'-'A'] = 3;
    while(cin>>n>>m&&n&&m){
        for(int i=0;ifor(int j=0;j){
            char ch = getchar();while(ch==' '||ch=='\n') ch = getchar();
            ditu[i][j] = ch;
            if(ch=='X') Fx = i,Fy = j;
            else if(ch=='*') Sx = i,Sy = j;
        }

        if(!bfs()) puts("The poor student is trapped!");
    }
    return 0;
}

扩展思考:我这里想到如果钥匙并非永久产品而是一次性,那该怎么办?

我感觉难点可能在于不止走一次回头路。

我一开始能想到就是:像多重背包操作一样,将问题转化为0-1,多个相同门和钥匙,对应将他们分类成不同颜色。额,好像不行,因为你不清楚那个钥匙对应哪个门即为最优。。。。

那判重函数多加一维度,有什么颜色钥匙,并且多一个钥匙数量的维度。


 超级密码HDU - 1226 

算法:BFS

思路:BFS向下操作,*进制数+构成数 然后 对N求余 得到 的余数,因为是要正整数倍,所以当余数为0则为答案。

K1%N = K2%N;(K1*C+M)%N = (K2*C+M)%N ;所以余数可以作为判重点。相同余数不需要重复入队。

用数组记录答案长度,因为BFS是有层次性,所以超过500就可以判负了。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int N,C,M;
int yS[17],ySlen;
int vis[5005],pre[5005],ans[5005],cnt[5005];

void init(){
    memset(vis,0,sizeof vis);
    memset(cnt,0,sizeof cnt);
    memset(pre,-1,sizeof pre);
    scanf("%d%d%d",&N,&C,&M);
    for(int i=0;i){
        char ch = getchar();while(ch == ' '|| ch == '\n') ch = getchar();
        if(ch>='0'&&ch<='9') yS[i] = ch - '0';
        else yS[i] = ch - 'A' + 10;
    }
    sort(yS,yS+M);
    ySlen = unique(yS,yS+M) - yS;
}

void showAns(int num){
    if(num == -1) return ;
    showAns(pre[num]);
    if(ans[num]<10) printf("%d",ans[num]);
    else printf("%c",ans[num] - 10 + 'A');
}

bool solve(){
    queue<int> q;
    for(int i=0;i){
        if(!yS[i]) continue;
        int tnt = yS[i] % N;
        if(vis[tnt]) continue;
        vis[tnt] = 1;cnt[tnt] = 1;
        ans[tnt] = yS[i];
        if(!tnt) return true;
        q.push(tnt);
    }
    while(!q.empty()){
        int now = q.front();q.pop();
        if(cnt[now]>500) return false;
        for(int i=0;i){
            int tnt  = (now*C + yS[i]) % N;
            if(vis[tnt]) continue;
            vis[tnt] = 1;cnt[tnt] = cnt[now] + 1;
            ans[tnt] = yS[i];pre[tnt] = now;
            if(!tnt) return true;
            q.push(tnt);
        }
    }
    return false;
}

int main()
{
    int _;scanf("%d",&_);
    while(_--){
        init();
        if(!N) {if(!yS[0]) puts("0");else puts("give me the bomb please");}
        else{
            if(!solve()) puts("give me the bomb please");
            else showAns(0),puts("");
        }
    }
    return 0;
}

 Different DigitsHDU - 1664 

算法:数论+BFS

思路:感觉这题集出得挺不错,每道题总与上一道题有关联。这一题和上一题一样,也是求N的正整数倍。所以也是余数下手。

但是这题巧妙在于,最优解是,答案需尽量少不同数字构成且最小。

这里牵扯的数论就是:

 很好理解:求10进制的余数,(*10+1)MOD,因为只加一个数字的求余存在一个循环,存在0则为答案。余数相同,就互减得到答案,互减将余数减掉就是N的整数倍了。

所以一个整数一定存在一个正整数倍的数,且由0和1的数构成。

所以,两不同数字就构成本题答案;

那么这题剩下就是求一个最优最小解,即不同数字的数量相同求最小。

分两层,第一层只用一个数叠加求,得到答案就是最优。得不到去第二层,循环从不同两个数字组合求出答案。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int inf = 2e9;

int N,ansLen;
bool vis[65536];
string ans,res;

bool gO(int x){
    memset(vis,false,sizeof vis);
    int tmp = 0;res = "";
    while(1){
        tmp = (tmp*10+x) % N;
        if(vis[tmp]) return false;
        vis[tmp] = true;
        res += '0' + x;
        if(!tmp){
            if(ansLen == inf||ans.length()>res.length()){
                ans = res;ansLen = res.length();return true;
            }
            return false;
        }
    }
}

int val[65536],pre[65536],cnt[65536];

void getAns(int x){
    if(x==-1) return ;
    getAns(pre[x]);
    res += '0' + val[x];
}

bool gT(int x,int y){
    memset(vis,false,sizeof vis);
    memset(pre,-1,sizeof pre);
    memset(cnt,0,sizeof cnt);
    queue<int> q;
    int fk,num[2]={x,y};
    if(x) { fk = x%N;vis[fk] = true,val[fk] = x,cnt[fk] = 1;q.push(fk); }
    fk = y%N; if(!vis[fk]) {vis[fk] = true,val[fk] = y,cnt[fk] = 1;q.push(fk);}
    while(!q.empty()){
        int now = q.front();q.pop();
        if(cnt[now]>ansLen) return false;
        for(int i=0;i<2;i++){
            fk = (now*10 + num[i]) % N;
            if(vis[fk]) continue;
            vis[fk] = true,val[fk] = num[i],cnt[fk] = cnt[now] + 1,pre[fk] = now;
            if(!fk){
                res = "";getAns(0);
                if(ansLen==inf||ans.length()>res.length()||(ans.length()==res.length()&&res<ans)){
                    ans = res;ansLen = res.length();
                    return true;
                }
                return false;
            }
            q.push(fk);
        }
    }
    return false;
}

int main()
{
    while(cin>>N&&N){
        int flag = 0; ansLen = inf;ans = "";
        for(int i=1;i<10;i++) if(gO(i)) flag = 1;
        if(flag) {cout<continue;}
        for(int i=0;i<10;i++) for(int j=i+1;j<10;j++) gT(i,j);
        cout<endl;
    }
    return 0;
}

 


 Pusher HDU - 2821 

算法:枚举+dfs(简单模拟)

题意:类似手机游戏,一副25*25的地图,有多个障碍物,障碍物由字母表示,a为一方块,b为内嵌的2方块,以此类推。

球体,要选取一个地方起步(这里我用全地图枚举),进行弹射必须有一个空格当空隙。

起步后,方向不变,直到撞到方块才停下,且推方块向后一步,方块数减一,剩余方块积累到后一步。后一步如果是地图外,则起步方块消失。

如果球飞出地图,则失败。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int n,m;
int ditu[26][26],sum;

int xx[4] = {0,0,1,-1};
int yy[4] = {1,-1,0,0};
char dire[5] = "RLDU";

int len;
char ans[2500];

void init(){
    sum = 0,len=0;
    memset(ditu,0,sizeof ditu);
    for(int i=0;ifor(int j=0;j){
        char ch = getchar();
        while(ch==' '||ch=='\n') ch = getchar();
        if(ch>='a'&&ch<='z') ditu[i][j] += ch - 'a' + 1, sum+= ditu[i][j];
    }
}
bool check(int a,int b,int c){
    int x = a + xx[c] , y = b + yy[c];
    if(x<0||x>=n||y<0||y>=m||ditu[x][y]!=0) return false;
    return true;
}

int check3(int x,int y){
    if(x<0||x>=n||y<0||y>=m) return 0;
    if(ditu[x][y]!=0) return 1;
    return 2;
}

bool check2(int &a,int &b,int c){
    while(1){
        a += xx[c],b += yy[c];
        int tnt = check3(a,b);
        if(tnt==0) return false;
        if(tnt==1) return true;
    }
}

bool dfs(int x,int y,int z){
    if(z==0) return true;
    for(int i=0;i<4;i++){
        if(!check(x,y,i)) continue;
        int a = x,b = y;
        if(!check2(a,b,i)) continue;
        //cout<
        bool flag = false;
        if(check3(a+xx[i],b+yy[i])!=0) flag = true;
        int fk = ditu[a][b],fkk,c;
        if(flag){
            fkk = ditu[a+xx[i]][b+yy[i]];
            ditu[a+xx[i]][b+yy[i]] += fk - 1;
            c = z - 1;
        }else c = z - fk;
        ditu[a][b] = 0;
        ans[len++] = dire[i];
        if(dfs(a,b,c)) return true;
        len--;
        ditu[a][b] = fk;
        if(flag) ditu[a+xx[i]][b+yy[i]] = fkk;
    }
    return false;
}

int main()
{
    while(cin>>m>>n){
        init();
        //cout<//for(int i=0;i
        int cp = 1;
        for(int i=0;ifor(int j=0;jif(ditu[i][j]==0&&dfs(i,j,sum)) {
                printf("%d\n%d\n",i,j);
                ans[len] = '\0';cout<endl;
                cp = 0;
        }
    }
    return 0;
}

 Tempter of the Bone IIHDU - 2128 

算法:BFS/DFS

题意:就是给一个有障碍的地图,图上有炸弹推,经过就可以捡上,不过只能捡一次。炸弹可以炸墙,不过算一步。

判重就简单的3维,坐标加炸弹。


 BFS

这里我BFS,直接模拟,找到答案直接输出,然后wrong了。

后面看别人代码,得知原因,因为,我直接将炸开炸弹,与进入墙体两步,结合在一层进行模拟;

容易导致BFS跨步错误,就像之前做过的一道题,情侣被鬼追,G - Nightmare Ⅱ  HDU - 3085 。这题我在kuangbin题集做了解析。

这里类似2步走,第二步vis标记会导致其他第一步入队而出现答案错误,wrong。

这里我是看别人代码学的操作,将vis移到出队再加,然后答案,通过ans记录,并进行求最短剪枝。这里就相当于将vis标记放到下一层去判断,虽然会增加入队冗余,但是ans优化也减了一部分。

一开始码的时候不知道这样对不对?不管先试了再说。然后就AC了。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int inf = 2e9;

int n,m,ans;
int Sx,Sy;

char ditu[9][9];
bool vis[9][9][600];

int xx[] = {1,-1,0,0};
int yy[] = {0,0,1,-1};

void init(){
    memset(vis,false,sizeof vis);
    ans = inf;
    for(int i=0;ifor(int j=0;j){
        char ch = getchar();while(ch==' '||ch=='\n') ch = getchar();
        ditu[i][j] = ch;
        if(ch=='S') Sx = i,Sy = j;
    }
}

struct node{
    int x,y,step,exp;
    bool state[9][9];
    node(int a,int b,int c,int d):x(a),y(b),step(c),exp(d) {
        memset(state,false,sizeof state);
    };
};

bool bfs(){
    queue q;q.push(node(Sx,Sy,0,0));
    while(!q.empty()){
        node p = q.front();q.pop();
        vis[p.x][p.y][p.exp] = true;
        if(p.x>=ans) continue;
        for(int i=0;i<4;i++){
            node now = p;
            now.x = p.x + xx[i] , now.y = p.y + yy[i],now.step++;
            if(now.x<0||now.x>=n||now.y<0||now.y>=m) continue;
            if(ditu[now.x][now.y]=='D') {ans = min(ans,now.step);continue;}
            if(ditu[now.x][now.y]=='X'&&!now.state[now.x][now.y]){
                if(now.exp==0) continue;
                else now.state[now.x][now.y] = true,now.exp--,now.step++;
            }
            if(ditu[now.x][now.y]>='0'&&ditu[now.x][now.y]<='9'&&!now.state[now.x][now.y]){
                now.state[now.x][now.y] = true;
                now.exp += ditu[now.x][now.y] - '0';
            }
            if(vis[now.x][now.y][now.exp]) continue;
            q.push(now);
        }
    }
    if(ans==inf) return false;
    else {cout<return true;}
}

int main()
{
    while(cin>>n>>m&&n&&m){
        init();
        if(!bfs()) puts("-1");
    }
    return 0;
}

后面想验证一下,到底是不是炸弹并步走,才导致的wrong,我就尝试将炸弹和入墙分为两步,再不同层进行。结果也AC。所以之前wrong就是BFS跨步走问题。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int inf = 2e9;

int n,m;
int Sx,Sy;

char ditu[9][9];
bool vis[9][9][600];

int xx[] = {1,-1,0,0};
int yy[] = {0,0,1,-1};

void init(){
    memset(vis,false,sizeof vis);
    for(int i=0;ifor(int j=0;j){
        char ch = getchar();while(ch==' '||ch=='\n') ch = getchar();
        ditu[i][j] = ch;
        if(ch=='S') Sx = i,Sy = j;
    }
}

struct node{
    int x,y,step,exp;
    int state[9][9];
    node(int a,int b,int c,int d):x(a),y(b),step(c),exp(d) {
        memset(state,0,sizeof state);
    };
};

bool bfs(){
    queue q;q.push(node(Sx,Sy,0,0));vis[Sx][Sy][0] = true;
    while(!q.empty()){
        node p = q.front();q.pop();
        if(ditu[p.x][p.y]=='X'&&p.state[p.x][p.y]==2){
            p.state[p.x][p.y]=1;
            p.step++;
            q.push(p);
            continue;
        }
        for(int i=0;i<4;i++){
            node now = p;
            now.x = p.x + xx[i] , now.y = p.y + yy[i],now.step++;
            if(now.x<0||now.x>=n||now.y<0||now.y>=m) continue;
            if(ditu[now.x][now.y]=='D') {cout<return true;}
            if(ditu[now.x][now.y]=='X'&&now.state[now.x][now.y]==0){
                if(now.exp==0) continue;
                else {
                    now.state[now.x][now.y] = 2,now.exp--;
                    if(vis[now.x][now.y][now.exp]) continue;
                    vis[now.x][now.y][now.exp] = true;
                    q.push(now);continue;
                }
            }
            if(ditu[now.x][now.y]>='0'&&ditu[now.x][now.y]<='9'&&!now.state[now.x][now.y]){
                now.state[now.x][now.y] = 1;
                now.exp += ditu[now.x][now.y] - '0';
            }
            if(vis[now.x][now.y][now.exp]) continue;
            vis[now.x][now.y][now.exp] = true;
            q.push(now);
        }
    }
    return false;
}

int main()
{
    while(cin>>n>>m&&n&&m){
        init();
        if(!bfs()) puts("-1");
    }
    return 0;
}

 IDEA*  

这里我DFS模拟,我决策函数用哈夫曼函数。然后模拟与上面BFS无疑,重点这里DFS也需要一步步来,因为想求得即最优。

可能存在没有答案,所以limit需要上限,8*8 = 64,64-2 = 62;然后取7个空作9炸弹,剩下55个空都是墙,然而63>55,所以55个墙为最坏情况,63+55 = 108步。上限为108;

(这里最坏情况假设,每次拿最近的炸弹必须用完之前的,每细想其他细节,所以大概就估算)

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int inf = 2e9;

int n,m;
int Sx,Sy,Fx,Fy;
int limit,nextd;

char ditu[9][9];
bool vis[9][9][600];
int state[9][9];

int xx[] = {1,-1,0,0};
int yy[] = {0,0,1,-1};

void init(){
    memset(vis,false,sizeof vis);
    memset(state,0,sizeof state);
    for(int i=0;ifor(int j=0;j){
        char ch = getchar();while(ch==' '||ch=='\n') ch = getchar();
        ditu[i][j] = ch;
        if(ch=='S') Sx = i,Sy = j;if(ch=='D') Fx = i,Fy = j;
    }
}

int check(int x,int y){
    return abs(x-Fx) + abs(y-Fy);
}

bool dfs(int x,int y,int step,int exp,int limit){
    int h = check(x,y);
    if(h+step>limit){
        nextd = min(h+step,nextd);
        return false;
    }
    if(h==0) {cout<return true;}
    if(ditu[x][y]=='X'&&state[x][y]==2){
        state[x][y] = 1;
        if(dfs(x,y,step+1,exp,limit)) return true;
        return false;
    }
    for(int i=0;i<4;i++){
        int a = x + xx[i],b = y + yy[i],c = step+1,d = exp;
        if(a<0||a>=n||b<0||b>=m) continue;
        int flag = 0;
        if(ditu[a][b]=='X'&&!state[a][b]){
            if(d==0) continue;
            else{
                d--,state[a][b] = 2,flag = 1;
                if(vis[a][b][d]) continue;
                vis[a][b][d] = true;
                if(dfs(a,b,c,d,limit)) return true;
                vis[a][b][d] = false;
                if(flag) state[a][b] = 0;
                continue;
            }
        }
        if(ditu[a][b]>='0'&&ditu[a][b]<='9'&&!state[a][b]) state[a][b] = 1,flag = 1,d += ditu[a][b] - '0';
        if(vis[a][b][d]) continue;
        vis[a][b][d] = true;
        if(dfs(a,b,c,d,limit)) return true;
        vis[a][b][d] = false;
        if(flag) state[a][b] = 0;
    }
    return false;
}

int main()
{
    while(cin>>n>>m&&n&&m){
        init();
        int flag = 0;vis[Sx][Sy][0] = true;
        for(limit = check(Sx,Sy);limit<=102;){
            nextd = inf;
            if(dfs(Sx,Sy,0,0,limit)) {flag = 1;break;}
            limit = nextd;
        }if(flag) continue;
        puts("-1");
    }
    return 0;
}

 Ali and BabaHDU - 4101 

算法:BFS + 博弈判奇偶

难点:题目在于理解。因为博弈,两人都是以自己最优的思路操作。所以胜负关键点很重要。

胜负关键点在于,谁最后打开宝藏包围圈谁输。

题意:一副地图,只有一个宝藏,然后存在石头推(以数字表示,<100),然后每人每步可以打碎一个石头。然后走路不算操作数。

思路:第一步:先标记从宝藏出发能达到的0点(包括宝藏自身也要标记),因为到达这里就表示已经赢了;这里如果标记点,存在于边界,就表示Ali不用破碎石头就能拿宝藏,所以Ali Win;

   第二步:获取走到胜负关键点的总操作数。(胜负关键点就是谁可以走到那些上一步的标记点,因为谁都不想帮别人破最后的石头,所以就会选择破其他无关石头,所以要记录所有外围石头数即总操作数);这里简单联想一下,就是这一步宝藏外围一定有一个石头包围圈即屏障,谁先打破谁输。

   第三步:第二步的重点在于记录石头数量,因为最后屏障是一个石头数量1的包围圈,这个包围圈石头数不用记录;然后所有外围石头数量总和判奇偶,奇数表示Baba要去破壁,所以Ali Win;反之同义。

失误:这里我wrong了;有两点:1)Baba Win 我写成了BaBa Win;2)第二步,我只用了(0,0)入队判断,其实不行,因为包围圈可能已经覆盖到边界了。所以要所有边界点入队判断;

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int n,m,Sx,Sy,ans;

int ditu[303][303];
bool vis[303][303],vis2[303][303];
int xx[] = {1,-1,0,0};
int yy[] = {0,0,1,-1};

void init(){
    memset(vis,false,sizeof vis);
    memset(vis2,false,sizeof vis2);
    ans = 0;
}

struct node{
    int x,y;
    node(int a,int b):x(a),y(b) {};
};

bool bfs(){
    queue q;
    q.push(node(Sx,Sy));vis2[Sx][Sy] = true;
    while(!q.empty()){
        node p = q.front();q.pop();
        if(p.x==0||p.x==n-1||p.y==0||p.y==m-1) return true;
        for(int i=0;i<4;i++){
            int x = p.x + xx[i],y = p.y + yy[i];
            if(x<0||x>=n||y<0||y>=m||ditu[x][y]!=0||vis2[x][y]) continue;
            q.push(node(x,y));
            vis2[x][y] = true;
        }
    }
    return false;
}

bool check(int x,int y){
    for(int i=0;i<4;i++) if(vis2[x+xx[i]][y+yy[i]]) return true;
    return false;
}

void bfs2(){
    queue q;

    for(int j=0;j){
        if(!vis[0][j]) q.push(node(0,j)),vis[0][j] = true,ans += ditu[0][j];
        if(!vis[n-1][j]) q.push(node(n-1,j)),vis[n-1][j] = true,ans += ditu[n-1][j];
    }
    for(int i=0;i){
        if(!vis[i][0]) q.push(node(i,0)),vis[i][0] = true,ans += ditu[i][0];
        if(!vis[i][m-1]) q.push(node(i,m-1)),vis[i][m-1] = true,ans += ditu[i][m-1];
    }

    while(!q.empty()){
        node p = q.front();q.pop();
        if(check(p.x,p.y)) {ans --;continue;}
        for(int i=0;i<4;i++){
            int x = p.x + xx[i],y = p.y + yy[i];
            if(x<0||x>=n||y<0||y>=m||vis[x][y]) continue;
            q.push(node(x,y));
            vis[x][y] = true,ans+=ditu[x][y];
        }
    }
    //cout<
    if(ans%2) puts("Ali Win");
    else puts("Baba Win");
}

void show(){
    for(int i=0;i){
        for(int j=0;j" ";cout<<endl;
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=0;ifor(int j=0;j){
            scanf("%d",&ditu[i][j]);
            if(ditu[i][j]==-1) Sx = i,Sy = j;
        }
        if(bfs()){
            puts("Ali Win");
        }else bfs2();
        //show();
    }
    return 0;
}

 Ancient Messages HDU - 3839 

算法:BFS

题意:就是给你一副图,图里面有很多埃及象形字,然后图像由像素0,1构成,1表示颜色黑,0则表示白。

难点:第一点在于怎么巧妙区别埃及象形字?   每个埃及象形字都是有闭合空间并且数量对于每个埃及象形字不同,所以可以从空白入手。

   第二点在于如何区分埃及象形字内的空白与外空白?  这里题目给出了关键点,就是每两个埃及象形字必不连接且不嵌套。并且黑色部分永远相连不断。

思路:预处理:这里使用十六进制代表二进制像素,所以需要还原。

   第一步:从边界开始读空白,进行白色BFS,把所有埃及象形字的外空白标记。

   第二步:从上到下从左到右,找埃及象形字,遇到则开始黑色BFS,每遇到白色没标记则开始白色BFS标记;重点记录,遇到几次空白,即这个埃及象形字有多少闭合空白空间。从而区分这个是什么埃及象形字;

     第三步:通过BFS得出的埃及象形字数量,然后字典输出即可AC。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int n,m,ditu[202][202],ans[8],cas=1;

int gg[] = {1,5,3,2,4,0};
char gk[] = {'A','D','J','K','S','W'};

int xx[] = {1,-1,0,0};
int yy[] = {0,0,1,-1};

bool vis[202][202];

struct node{
    int x,y;
    node(int a,int b):x(a),y(b) {};
};

void bfs_white(int a,int b){
    queue q;
    q.push(node(a,b)),vis[a][b] = true;
    while(!q.empty()){
        node p = q.front();q.pop();
        for(int i=0;i<4;i++){
            int x = p.x + xx[i],y = p.y + yy[i];
            if(x<0||x>=n||y<0||y>=m||ditu[x][y]||vis[x][y]) continue;
            q.push(node(x,y)),vis[x][y] = true;
        }
    }
    return ;
}

void bfs_black(int a,int b){
    queue q;
    int acer = 0;
    q.push(node(a,b)),vis[a][b] = true;
    while(!q.empty()){
        node p = q.front();q.pop();
        for(int i=0;i<4;i++){
            int x = p.x + xx[i],y = p.y + yy[i];
            if(x<0||x>=n||y<0||y>=m||vis[x][y]) continue;
            if(ditu[x][y]) q.push(node(x,y)),vis[x][y] = true;
            else bfs_white(x,y),acer++;
        }
    }
    ans[acer]++;
    return ;
}

void show(){
    for(int i=0;i){
        for(int j=0;j" ";cout<<endl;
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m)){
        if(!n&&!m) break;
        for(int i=0;ifor(int j=0,z=0;j){
            char ch = getchar();
            while(ch==' '||ch=='\n') ch = getchar();
            int num;
            if(ch>='a'&&ch<='z') num = ch-'a'+10;
            else num = ch-'0';
            if(num>=8) ditu[i][z++] = 1,num-=8;
            else ditu[i][z++] = 0;
            if(num>=4) ditu[i][z++] = 1,num-=4;
            else ditu[i][z++] = 0;
            if(num>=2) ditu[i][z++] = 1,num-=2;
            else ditu[i][z++] = 0;
            if(num>=1) ditu[i][z++] = 1;
            else ditu[i][z++] = 0;
        }m*=4;
        //show();

        memset(ans,0,sizeof ans);
        memset(vis,false,sizeof vis);

        //边界找0
        for(int j=0;j){
            if(!vis[0][j]&&ditu[0][j]==0) bfs_white(0,j);
            if(!vis[n-1][j]&&ditu[n-1][j]==0) bfs_white(n-1,j);
        }
        for(int i=0;i){
            if(!vis[i][0]&&ditu[i][0]==0) bfs_white(i,0);
            if(!vis[i][m-1]&&ditu[i][m-1]==0) bfs_white(i,m-1);
        }

        //找图像
        for(int i=0;ifor(int j=0;jif(!vis[i][j]&&ditu[i][j]) bfs_black(i,j);

        printf("Case %d: ",cas++);
        for(int i=0;i<6;i++) for(int j=0;j"%c",gk[i]);
        puts("");
    }
    return 0;
}

 Booksort HDU - 1685

算法:IDA* /dfs

题意:给你一个打乱顺序的序列,每次操作可以选择一个长度的 子连续序列 插入任意位置,问最少需要多少次操作变成升序序列。

  条件就是序列的数字一定连续的数字,即 1,2,3,4,5这样的数字。然后错过4次操作的答案,一律为5 or more

  举例:5 4 3 2 1  ------>  3 2 5 4 1 -------> 3 4 1 2 5 ------------> 1 2 3 4 5   只要3步。

思路一:题意明确只要4步以内就行了。那就使用 IDA*的dfs进行limit+1慢慢推进,直到得出答案。

   难点:启发函数的设定。

   解决:序列分块,连续的为一块,即1 4 2 3 5 序列 分成 4块 1、 4 、 23、 5;

      这里注意, 如果第一块为1,其本身不应该算进去,因为至始至终都不会移动这一块;最末端块同理;因为 1 和 5 本身就在其最终位置上永远不需要动,

      所以 只要改变 4,23两块就行了,即需要操作的块数为2。

          然后每次操作都会影响3块的后缀块变化,即分离块的前一块的、分离块本身与被插入点前面那块这3块的后缀;理想情况下,每次改变都使得其连接成一块,即每次操作减少3块。

      所以1 4 2 3 5 序列 启发函数 h(x) = ceil(操作块数/3),向下取整,即是1。

#include 
#include 
#include 
#include 
#include 

using namespace std;

const int INF = 2e9;

int n,num[20];

void init(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>num[i];
    num[0]=0;num[n+1]=n+1;
}

void show(){
    for(int i=1;i1;i++){
        cout<" ";
    }cout<<endl;
}

int forLimit(){
    double ans = 0;
    for(int i=0;i<=n;i++) if(num[i]+1!=num[i+1]) ans++;
    return ceil(ans/3);
}

bool dfs(int step,int limit){
    int h = forLimit();
    if(h+step>limit) return false;
    if(h==0){
        cout<endl;
        //show();
        return true;
    }
    int temp[20];
    memcpy(temp,num,sizeof(num));
    for(int i=1;i<=n;i++) for(int len=1;len<=n-i+1;len++) for(int pos=1;pos<=n-len+1;pos++) if(pos!=i){
        for(int j=0;jj];
        for(int p=1,j=1;j<=n;){
            if(j];
            else if(j==pos) j+=len;
            else if(p==i) p+=len;
            else num[j++] = temp[p++];
        }
        if(dfs(step+1,limit)) {
                //memcpy(num,temp,sizeof(temp));show();
                return true;
        }
    }
    memcpy(num,temp,sizeof(temp));
    return false;
}

int main()
{
    int _;cin>>_;
    while(_--){
        init();
        for(int limit=forLimit();;limit++){
            if(limit>4) {puts("5 or more");break;}
            if(dfs(0,limit)) break;
        }
    }
}

思路二:观看大神博客https://blog.csdn.net/ilsswfr/article/details/52012467

    类似于双向遍历bfs一样。

    第一步:从初始序列开始dfs两步,得出答案为解。用set记录所有得出的序列。

    第二部:从目标序列开始dfs两步,如果出现的序列在set里面则步数+2为解。

//从初始情况搜索两层并记录每次状态,这两层中如果有解,直接输出
//若是无解,从有序状态往回搜一层和两层,
//如果能达到与之前的相同的状态就是有解。
 
#include
#include
#include
#include
#include
#include<string>
#include
#include
#include<set>
#include
#include
#include
#define pi acos(-1.0)
#define inf 1<<29
#define INF 0x3f3f3f3f
#define zero 1e-8
 
using namespace std;
int step, n;
int arr[100];
 
int fin[100];
 
set<string> ff;
 
int makenum(int num, char *s, int &k)
{
 
    int i = 0, now[5] = {};
    for (; num;) {
        now[++i] = num % 10;
        num /= 10;
    }
    for (int j = i; j; j--) {
        s[k++] = now[j] + '0';
    }
    return k;
 
}
 
string makestr(int *s, int num)  //样式
{
 
    string aa;
    char a[100];
    int k = 0;
    for (int i = 0; i < num; ++i) {
        makenum(s[i], a, k);
        a[k++] = ' ';
    }
    a[k] = '\0';
    return a;
 
}
bool compare(int *s)
{
    for (int i = 0; i < n; ++i)
        if (fin[i] != s[i]) return false;
 
    return true;
}
void show(int *s)
{
    for (int i = 0; i < n; ++i)
        cout << s[i];
    cout << endl;
}
void cop(int *en, int *be)
{
    for (int i = 0; i < n; ++i)
        en[i] = be[i];
}
void dfs1(int *tem, int cnt, int now)
{
    string str = makestr(tem, n);
    ff.insert(str);
    if (step != inf) return ;
    if (cnt == now) {
        if (compare(tem)) {
            step = cnt;
        }
 
        return ;
    }
 
    if (cnt < now) return ;
    int tt[100];
 
    for (int j = 0; j < n; ++j) {
 
        int z;
        for (z = 0; z < j; ++z)
            tt[z] = tem[z];
        for (int len = 1; len < n - j; ++len) {
            cop(tt, tem);
 
            int f = z + len;
            for (int sert = 0; sert < n - j - len; ++sert) {
                tt[z + sert] = tem[f + sert];
 
                if (f + sert + 1 < n - j - len
                        && tem[f + sert + 1] == tt[z + sert] + 1) continue;
 
                for (int kk = 1; kk <= len; ++kk)
                    tt[z + sert + kk] = tem[j + kk - 1];
 
                string str = makestr(tt, n);
                ff.insert(str);
                dfs1(tt, cnt, now + 1);
            }
 
        }
    }
}
 
void dfs2(int *tem, int cnt, int now)
{
 
    if (step != inf) return ;
    string str = makestr(tem, n);
    if (cnt == now) {
        if (ff.find(str) != ff.end())
            step = 0;
        return ;
    }
 
    int tt[100];
 
    for (int j = 0; j < n; ++j) {
 
        int z;
        for (z = 0; z < j; ++z)
            tt[z] = tem[z];
        for (int len = 1; len < n - j; ++len) {
            cop(tt, tem);
 
            int f = z + len;
            for (int sert = 0; sert < n - j - len; ++sert) {
                tt[z + sert] = tem[f + sert];
 
                if (f + sert + 1 < n - j - len
                        && tem[f + sert + 1] == tt[z + sert] + 1) continue;
 
                for (int kk = 1; kk <= len; ++kk)
                    tt[z + sert + kk] = tem[j + kk - 1];
                dfs2(tt, cnt, now + 1);
            }
 
        }
    }
}
 
void makefin()
{
    for (int i = 0; i < n; ++i)
        fin[i] = i + 1;
}
 
int main()
{
    int t;
    cin >> t;
    for (; t--;) {
        scanf("%d", &n);
 
        for (int i = 0; i < n; ++i) {
            scanf("%d", &arr[i]);
        }
        makefin();
        step = inf;
 
        for (int i = 0; i <= 2; ++i) {
            step = inf;
            ff.clear();
            dfs1(arr, i, 0);
            if (step != inf) {
                printf("%d\n", step);
                break;
            }
        }
        if (step == inf) {
            for (int i = 1; i <= 2; ++i) {
 
                dfs2(fin, i, 0);
                if (step != inf) {
                    printf("%d\n", i + 2);
                    break;
                }
            }
            if (step == inf) printf("5 or more\n");
        }
    }
    return 0;
}


 BeatHDU - 2614 

算法:dfs

题意:给一个矩阵Tij,行为i,列为j,Tij数字为在做完第i题之后,会花费Tij时间去做第j题;这里题的难度与时间正相关。

   条件:1.acm大佬只做难度递增的题,即时间需要递增;

      2.永远从第一题开始做起,而且花费时间永远为0

思路:就是从第一行开始往下dfs,需要每次都做更难或者相同难度的题。

#include 
#include 
#include 
#include 
#include 

using namespace std;

int T[20][20],n,ans;
bool ck[20];

void init(){
    memset(ck,false,sizeof ck);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) cin>>T[i][j];
    ans=1;
}

//x:上一题  y:上一题花费时间 z:总做题数
void dfs(int x,int y,int z){
    if(ans==n) return;
    if(z==n){ans = n;return ;}
    int flag = 1;
    for(int j=2;j<=n;j++) if(!ck[j]&&T[x][j]>=y){
        flag = 0;
        ck[j]=true;
        dfs(j,T[x][j],z+1);
        ck[j]=false;
    }
    if(flag) ans = max(ans,z);
    return ;
}

int main()
{
    while(cin>>n){
        init();
        ck[1] = true;
        dfs(1,0,1);
        cout<endl;
    }
}


Roll The CubeHDU - 3309 

算法:bfs

题意:给一个外边界都是墙的地图,然后里边也有墙,即障碍物;给你两个球,可以上下左右滚动一格,然后每次操作都是同步,撞墙就不能动,两个球不能重叠。地图必有两个洞,目标就是算将两个球滚进洞里    的最短操作数。

思路/难点:简单的模拟题,用BFS。

     第一:BFS需要记录每一操作状态;状态有:球1坐标、球2坐标、球1进洞状态、球2进洞状态、洞1状态、洞2状态、步数。

        需要维护球进洞状态:因为如果球进洞了的话,不管这么滚,坐标都不动,并且,当一个球进洞了,那么两个球是可以重叠到一个坐标上。

        需要维护洞状态:判断球是否能进此洞。

     第二:排重:需要状态数组进行避免重复操作。这里因为两个球,所以就用四维数组,即球1坐标、球2坐标进行排重。不管球是否进洞都可以判断;

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int n,m;
bool mp[24][24];
bool ck[24][24][24][24];
int bx[3],by[3];
int hx[3],hy[3];
int xx[]={0,0,1,-1};
int yy[]={1,-1,0,0};

void init(){
    memset(mp,true,sizeof mp);
    memset(ck,true,sizeof ck);
    cin>>n>>m;
    int bNum=0,hNum=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            char ch = getchar();
            while(ch=='\n'||ch==' ') ch=getchar();
            if(ch=='*') mp[i][j]=false;
            else if(ch=='B') bx[bNum]=i,by[bNum++]=j;
            else if(ch=='H') hx[hNum]=i,hy[hNum++]=j;
    }
}

void show(){
    cout<" "<endl;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) cout<" ";cout<<endl;
    }cout<<endl;
    cout<0]<<" "<0]<<" "<1]<<" "<1]<<endl;
    cout<0]<<" "<0]<<" "<1]<<" "<1]<<endl;
    cout<<endl;
}

struct node{
    int x1,y1,x2,y2,bS1,bS2,hS1,hS2,step;
    node(int xx1,int yy1,int xx2,int yy2,int bbS1,int bbS2,int hhS1,int hhS2,int stepp)
    :x1(xx1),y1(yy1),x2(xx2),y2(yy2),bS1(bbS1),bS2(bbS2),hS1(hhS1),hS2(hhS2),step(stepp) {};
};

void change(int &x,int &y,int bS,int i){
    if(bS==1) return ;
    if(mp[x+xx[i]][y+yy[i]]) x += xx[i] , y += yy[i];
    return;
}

int check(int x,int y,int &hS1,int &hS2){
    if(hS1==0&&x==hx[0]&&y==hy[0]) {hS1=1;return 1;}
    if(hS2==0&&x==hx[1]&&y==hy[1]) {hS2=1;return 1;}
    return 0;
}

int bfs(){
    queue q;
    q.push(node(bx[0],by[0],bx[1],by[1],0,0,0,0,0));ck[bx[0]][by[0]][bx[1]][by[1]] = false;
    while(!q.empty()){
        node p = q.front();q.pop();
        for(int i=0;i<4;i++){
            int x1=p.x1,y1=p.y1,x2=p.x2,y2=p.y2,bS1=p.bS1,bS2=p.bS2,hS1=p.hS1,hS2=p.hS2;
            change(x1,y1,bS1,i);change(x2,y2,bS2,i);
            if(bS1==0&&bS2==0&&x1==x2&&y1==y2) continue;
            if(ck[x1][y1][x2][y2]==false) continue;
            ck[x1][y1][x2][y2]=false;
            if(bS1==0) bS1 = check(x1,y1,hS1,hS2);
            if(bS2==0) bS2 = check(x2,y2,hS1,hS2);
            if(hS1&&hS2) return p.step+1;
            q.push(node(x1,y1,x2,y2,bS1,bS2,hS1,hS2,p.step+1));
        }
    }
    return -1;
}

int main()
{
    int _;cin>>_;
    while(_--){
        init();//show();
        int ans = bfs();
        if(ans==-1) puts("Sorry , sir , my poor program fails to get an answer.");
        else cout<endl;
    }
}


 JerboasHDU - 2437 

算法:优先队列+BFS

题意:就是一个老鼠,他有很多个洞,TP,临时和永久两种;给你一个洞与洞的单向路径图,而且没有自身到自身的路径;给出数字K,问从S洞开始,到任意一个永久洞的最短路,并且路径长度必须为K的倍数。

思路:这里求最短路径,立马就想到BFS。

   1.因为洞S题目已经给出了,初始洞一定为临时洞。

           2.这里没有明确表示两个洞之间只有唯一路径,所以需要考虑两个洞存在不同长度的路径,使用二维vector存储。

           3.判重关键点,路径长度和K倍数,所以只要求路径长度对K求余的余数就能判断,K最大为1000 所以判重数组,二维只要开到1000左右,即vis[点数量][1001]。

           4.这里因为并不是简单判断最短路径,并且需要K倍数,对路径排序并没有用,所以需要优先队列,对入队的最短路进行优先出队。

我的出错点:这里我受到之前做题的影响,因为我上一道题的bfs是步骤,每次只加一,所以把判断放在了入队前也没问题。然而对于现在这题来说不行。所以需要入队完,从队列取出来再判断,再加上是优先队列,每次取出来都是最短路,只要达到要求就是答案。

#include 
#include 
#include 
#include 
#include 
#include 
#include <set>

using namespace std;

int n,m,s,k;
char dong[1002];

struct guan{
    int e,f;
    guan(int ee,int ff):e(ee),f(ff) {};
};
vector v[1002];

bool vis[1002][1001];

void init(){
    cin>>n>>m>>s>>k;
    for(int i=1;i<=n;i++){
        char ch = getchar();while(ch=='\n' || ch==' ') ch = getchar();
        dong[i] = ch;
    }
    for(int i=1;i<=n;i++) v[i].clear();
    memset(vis,false,sizeof vis);
    for(int i=0;i){
        int a,b,c;cin>>a>>b>>c;
        v[a].push_back(guan(b,c));
    }
}

struct node{
    int x,y;
    node(int xx,int yy):x(xx),y(yy) {};
    bool operator < (const node &a) const{
        if(y==a.y ) return x>a.x;
        return y>a.y;
    }
};

bool bfs(){
    priority_queue q;
    vis[s][0] = true;q.push(node(s,0));
    while(!q.empty()){
        node p = q.top();q.pop();
        int x = p.x , y = p.y;
        if(dong[x]=='P'&&y%k==0){
                cout<" "<endl;
                return true;
            }
        for(int i=0;i){
            int e = v[x][i].e , f = v[x][i].f + y;
            if(vis[e][f%k]) continue;
            vis[e][f%k] = true;
            q.push(node(e,f));
        }
    }
    return false;
}

int main()
{
    int _;cin>>_;
    for(int i=1;i<=_;i++){
        init();
        cout<<"Case "<": ";
        if(!bfs()) cout<<"-1 -1"<<endl;
    }
}


 Open the LockHDU - 1195 

算法:BFS

题意:给一个4位数,然后对位数上面的数进行操作,可以加一减一,9+1 = 1,1-1=9;可以与相邻的位数互换。

思路:简单的模拟题。因为这里只给了四位数,判重方法直接用vis[10][10][10][10]记录4个位数变化就好了

#include 
#include 
#include 
#include 
#include 
#include 
#include <set>

using namespace std;

int n[5],e[5];
bool vis[10][10][10][10];

void init(){
    memset(vis,true,sizeof vis);
    int num;cin>>num;
    for(int i=3;i>=0;i--){
        n[i] = num%10;
        num/=10;
    }
    cin>>num;
    for(int i=3;i>=0;i--){
        e[i] = num%10;
        num/=10;
    }
}

struct node{
    int t[5],step;
    node(int *a,int sstep){
        for(int i=0;i<4;i++) t[i] = a[i];
        step = sstep;
    }
    bool check(){
        if(vis[t[0]][t[1]][t[2]][t[3]]) {
            vis[t[0]][t[1]][t[2]][t[3]] = false;
            return true;
        }
        return false;
    }
    void left(int i){
        int a = t[i];
        t[i] = t[i-1];
        t[i-1] = a;
    }
    void right(int i){
        int a = t[i];
        t[i] = t[i+1];
        t[i+1] = a;
    }
    bool ansCheck(){
        for(int i=0;i<4;i++) if(t[i]!=e[i]) return false;
        return true;
    }
};

int bfs(){
    queue q;
    q.push(node(n,0));
    vis[n[0]][n[1]][n[2]][n[3]] = false;

    while(!q.empty()){
        node p = q.front();q.pop();

        if(p.ansCheck()) return p.step;

        for(int i=0;i<4;i++){
            //==============================
            node temp = p;temp.step++;
            if(temp.t[i]==9) temp.t[i] = 1;
            else temp.t[i]++;
            if(temp.check()) q.push(temp);
            //==============================
            temp = p;temp.step++;
            if(temp.t[i]==1) temp.t[i] = 9;
            else temp.t[i]--;
            if(temp.check()) q.push(temp);
            //==============================
            temp = p;temp.step++;
            if(i!=0){
                temp.left(i);
                if(temp.check()) q.push(temp);
            }
            //==============================
            temp = p;temp.step++;
            if(i!=3){
                temp.right(i);
                if(temp.check()) q.push(temp);
            }
        }
    }

    return -1;
}

void show(){
    for(int i=0;i<4;i++) cout<" ";cout<<endl;
    for(int i=0;i<4;i++) cout<" ";cout<<endl;
}

int main()
{
    int _;cin>>_;
    while(_--){
        init();//show();
        cout<endl;
    }
}


 Pushing Boxes POJ - 1475 

算法:BFS

题意:推箱子游戏,游戏规则:人只能有两个操作,一个就是走,另一个就是推箱子。目标就是把箱子推到目标位置。然后求 推操作数量最小 为前提 的 最短路径。

思路1:这个是我想的,直接用优先队列,每个点记录,人位置、箱子位置、推的数量、步数;然后优先 推数量 最小 在前面,然后相同则步数最小。

       排重这里我用 [ 人X坐标 ] [ 人Y坐标 ] [ 箱子X坐标 ] [ 箱子Y坐标 ] [ 推数量 ] 五维数组维护,然后TL了。后面把推数量去掉,就ac了。

  (这里想了一下,优先队列已经维护了推数量最少,排重数组不需要维护,因为之前出现过推数量,在人与箱子相对位置的情况下,必为最优;即先存在为最优)

   然后唯一注意的点是,这里输出答案 两个之间有一个空行。没留意,wrong了很多次。看了人家代码才发现。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int INF = 2e9;

int n,m;
int Sx,Sy,Bx,By,Tx,Ty;
bool mp[21][21];
bool vis[21][21][21][21];
int xx[] = {0,0,-1,1};
int yy[] = {1,-1,0,0};
char fch[] = {'e','w','n','s'};
char bch[] = {'E','W','N','S'};

bool checkXY(int a,int b){
    if(a<=0||b<=0||a>n||b>m) return false;
    return mp[a][b];
}

struct node{
    int a,b,c,d,step,p;
    string ans;
    node(int aa,int bb,int cc,int dd,string ss,int sstep,int pp):a(aa),b(bb),c(cc),d(dd),ans(ss),step(sstep),p(pp) {};
    bool check(){
        if(c==Tx&&d==Ty) return true;
        return false;
    }
    bool operator < (const node &x) const{
        if(p==x.p) return step>x.step;
        return p>x.p;
    }
};

void init(){
    memset(mp,true,sizeof mp);
    memset(vis,true,sizeof vis);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        char ch=getchar();while(ch=='\n') ch=getchar();
        if(ch=='#') mp[i][j] = false;
        else if(ch=='S') Sx=i,Sy=j;
        else if(ch=='B') Bx=i,By=j;
        else if(ch=='T') Tx=i,Ty=j;
    }
}

void show(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) cout<" ";cout<<endl;
    }
}

bool bfs(){
    priority_queue q;
    q.push(node(Sx,Sy,Bx,By,"",0, 0));
    vis[Sx][Sy][Bx][By] = false;
    while(!q.empty()){
        node p = q.top();q.pop();
        if(p.check()){
            cout<endl;
            return true;
        }
        for(int i=0;i<4;i++){
            node u = p;u.a += xx[i],u.b += yy[i];
            if(!checkXY(u.a,u.b)) continue;
            bool flag = false;
            if(u.a==u.c&&u.b==u.d) flag = true;
            if(flag){
                u.c += xx[i],u.d += yy[i];
                if(!checkXY(u.c,u.d)) continue;
                if(!vis[u.a][u.b][u.c][u.d]) continue;
                vis[u.a][u.b][u.c][u.d] = false;
                u.ans+=bch[i];
                u.step++;
                u.p++;
                q.push(u);
            }else{
                if(!vis[u.a][u.b][u.c][u.d]) continue;
                 vis[u.a][u.b][u.c][u.d] = false;
                 u.ans+=fch[i];
                 u.step++;
                 q.push(u);
            }
        }
    }
    return false;
}

int main()
{
    int cas = 1;
    while(cin>>n>>m&&n&&m){
        cout<<"Maze #"<endl;
        init();
        if(!bfs()) puts("Impossible.");
        cout<<endl;
    }
}

思路2:这个是看别人博客学的。http://t.zoukankan.com/xiaoguapi-p-10389623.html

    大意就是进行一次箱子到目标位置的BFS求最短路径,然后在箱子BFS内部嵌套一个人推箱子的BFS求人到箱子位置的最短路径。

   重点:1.箱子每一步,都要去BFS判断人能不能走到箱子上一步位置,不行则箱子走不了。2.判重,外围bfs只需要维护箱子位置就行,内部每一次BFS判重也只维护人的位置即可。

            代码详情看大神博客。