<<表示左移,如a<<1表示将a的二进制左移一位,加一个0,&0xff表示取最后8个字节,如a&0xff表示取a表示的二进制中最后8个数字组成一个新的二进制数,| 运算符表示对2个数的二进制位进行比较,只要2个之中有一个这个位是1,则2者进行或运算之后得到的那个二进制数相应的位也是1。

因此,可以将3个数进行左移操作,然后再进行或运算,最后得到一个数,然后就可以通过这个数进行右移操作再进行&0xff操作就能得到原来的数。由于直接对二进制进行操作,因而速度极快。

有读者可能会说可以直接开一个结构体,将3个数都归属于一个结构体中,这样当然可以,不过速度远慢于上述操作。

对于POJ3523---The Morning after Halloween(UVa 1601)题目,运用结构体的运行时间是4672MS,而用第一个方法的运行时间是3719MS。所以明显第一个方法远远优于结构体方法。

以下为题目完整代码:

///本题可以通过对空白格进行构图,从而减少广搜时所需的操作时间。可以对每个空格进行编号,然后记录开始状态和结束状态,从开始状态对图进行广搜


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

const int maxs=20;
const int maxn=150;
const int dr[]= {1,-1,0,0,0}; /// 4 moves, plus "no move"
const int dc[]= {0,0,1,-1,0};

inline int ID(int a, int b, int c)
{
    return (a<<16)|(b<<8)|c;///由于int 类型保存8个字节,所以这个操作将a放到了最前面的8个字节,b放到了中间的8个字节,c放到了最后的8个字节
}

int s[3],t[3];///s表示开始位置,t表示终点位置
int degree[maxn],G[maxn][5];
inline bool conflict(int a, int b, int a2, int b2)
{
    return a2 == b2 || (a2 == b && b2 == a);///第一个表示a,b进入同一空格,第二个表示2者交换位置
}
int d[maxn][maxn][maxn];///表示所走的总步数


int bfs()
{
    queue<int> q;
    q.push(ID(s[0],s[1],s[2]));
    memset(d,-1,sizeof(d));
    d[s[0]][s[1]][s[2]]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        int a=(u>>16)&0xff,b=(u>>8)&0xff,c=u&0xff;
        if(a==t[0]&&b==t[1]&&c==t[2]) return d[a][b][c];
        for(int i=0;i<degree[a];i++)///从第一个鬼开始,查找连通的空格
        {
            int a2=G[a][i];
            for(int j=0;j<degree[b];j++)
            {
                int b2=G[b][j];
                if(conflict(a,b,a2,b2)) continue;
                for(int k=0;k<degree[c];k++)
                {
                    int c2=G[c][k];
                    if(conflict(a, c, a2, c2)) continue;
                    if(conflict(b, c, b2, c2)) continue;
                    if(d[a2][b2][c2]!=-1) continue;///判断是否被访问过
                    d[a2][b2][c2]=d[a][b][c]+1;
                    q.push(ID(a2,b2,c2));
                }
            }
        }
    }
    return -1;
}

int main()
{
    int w,h,n;
    while(~scanf("%d%d%d\n",&w,&h,&n)&&w)
    {
        char maze[20][20];
        for(int i = 0; i < h; i++)
            fgets(maze[i], 20, stdin);

        int cnt,x[maxn],y[maxn],id[maxs][maxs];///cnt 用于计数,id数组用于记录空格所处位置及其编号
        cnt=0;
        for(int i=0; i<h; i++)
            for(int j=0; j<w; j++)
                if(maze[i][j]!='#')
                {
                    x[cnt]=i;
                    y[cnt]=j;
                    id[i][j]=cnt;
                    if(islower(maze[i][j])) s[maze[i][j]-'a']=cnt;
                    else if(isupper(maze[i][j])) t[maze[i][j]-'A']=cnt;
                    cnt++;
                }

        for(int i=0; i<cnt; i++) ///建图
        {
            degree[i]=0;
            for(int dir=0; dir<5; dir++)
            {
                int nx=x[i]+dr[dir];
                int ny=y[i]+dc[dir];
                if(maze[nx][ny]!='#') G[i][degree[i]++]=id[nx][ny];
            }
        }
        ///加入假鬼,使3种情况都能按照3个鬼的bfs进行,由于假鬼开始就待在终点,所以不会对结果产生影响
        if(n <= 2)
        {
            degree[cnt] = 1;
            G[cnt][0] = cnt;
            s[2] = t[2] = cnt++;
        }
        if(n <= 1)
        {
            degree[cnt] = 1;
            G[cnt][0] = cnt;
            s[1] = t[1] = cnt++;
        }
        printf("%d\n",bfs());
    }
    return 0;
}