2011-0014
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
BFS[[BR]]
题意是给一张地图(最大10*10),上面有两个出口,一些石块,一些记忆碎片(不超过5个),三颗棋子(爱丽丝,镜像,骑士)。
棋子均不能出围墙[[BR]]
骑士面朝给定的方向每回合向前移动一步,如果不能向前移动,则转身(原地不动,消耗一回合)[[BR]]
爱丽丝每回合可向上下左右移动一步,且必须移动[[BR]]
镜像与爱丽丝的移动方向相反,移动一步(如果前方是棋子或石块或围墙则不移动)[[BR]]
只有爱丽丝可以捡记忆碎片[[BR]]
每回合移动顺序:爱丽丝 镜像 骑士[[BR]]
目标:爱丽丝捡起所有记忆碎片之后与镜像同一回合在出口上[[BR]]
注意:[[BR]]
1.爱丽丝与镜像均在出口上时迷宫解除,这一回合骑士来不及动[[BR]]
2.爱丽丝与镜像均在出口上时迷宫解除,不能继续捡记忆碎片[[BR]]
[[BR]]
本题的解法为BFS,状态数最多为10*10(爱丽丝的位置)*10*10(镜像的位置)*20(骑士的位置,最多有20种状态)*32(用二进制存储记忆碎片获得情况)=6400000[[BR]]
由于状态的总量较大,且组成状态的变量多,为了节省内存,可以将其压缩成一个变量[[BR]]
by luyi[[BR]]
{{{
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int O = 31;
char s[20][20],ss;
int map[20][20],f[10000][20][32],list[6400000],kx1[30],ky1[30],tk[30],len;
int m,n,tot,l,tt,kx,ky,ax,ay,bx,by,p,kk,dx,dy,t1,temp,te,flag,ta,tb;
//int pre[6400000],pret,last;
int bianma(int te,int k,int p)
{
return ((te+k*10000) << 5) + p;
}
void jiema(int k)
{
p = k & O;
k = k >> 5;
kk = f[te = (k % 10000)][temp = (k/10000)][p]+1;
tb = te % 100;
ta = te / 100;
by = tb % 10 + 1;
bx = tb / 10 + 1;
ay = ta % 10 + 1;
ax = ta / 10 + 1;
t1 = temp;
if ((++temp) == len) temp = 0;
kx = kx1[temp];
ky = ky1[temp];
}
void fangxiang(char s)
{
dx = dy = 0;
if (s == 'E') dy = 1; else
if (s == 'W') dy = -1; else
if (s == 'N') dx = -1; else
if (s == 'S') dx = 1;
}
int fff(char s,int x,int y)
{
if (s == '*') return 0;
if (s == 'R') return -1;
if (s == 'K') {kx = x;ky = y;return 0;}
if (s == 'A') {ax = x;ay = y;return 0;}
if (s == 'B') {bx = x;by = y;return 0;}
if (s == 'E') return -2;
if (s == 'M') {tot++;return (1<<(tot-1));}
}
void step()
{
kx1[len] = kx;
ky1[len++] = ky;
kx += dx;
ky += dy;
}
void walk()
{
while (map[kx][ky] != -1) step();
kx -= dx;
ky -= dy;
dx = -dx;
dy = -dy;
}
void knight()
{
kx1[0] = kx;
ky1[0] = ky;
len = 1;
fangxiang(ss);
kx += dx;
ky += dy;
walk();
walk();
while (!((kx == kx1[0])&&(ky == ky1[0]))) step();
for(int i = 0;i < len;i++) tk[i] = 10*kx1[i]+ky1[i]-11;
}
void work(int ax,int ay,int bx,int by,int p)
{
//pret = l-1;
if ((ax == bx)&&(ay == by)) return;
if (map[ax][ay] == -1) return;
if (map[ax][ay] > 0) p = (p | map[ax][ay]);
te = ax*1000+ay*100+bx*10+by - 1111;
if (f[te][temp][p] >= 0) return;
if ((ax == kx1[t1])&&(ay == ky1[t1])) return;
if ((bx == kx1[t1])&&(by == ky1[t1])) return;
if ((map[ax][ay] == -2)&&(map[bx][by] == -2))
{
if (p == tot)
{
flag = kk;
//last = tt+1;
l = tt + 1000;
} else
{
f[te][temp][p] = 0;
return;
}
}
if ((ax == kx)&&(ay == ky)) return;
if ((bx == kx)&&(by == ky)) return;
list[++tt] = bianma(te,temp,p);
//pre[tt] = pret;
f[te][temp][p] = kk;
}
//void print(int pret)
//{
// if (pret > 0) print(pre[pret]);
// jiema(list[pret]);
// printf("%d %d %d %d %d %d\n",ax,ay,bx,by,kx1[t1],ky1[t1]);
//}
void begin()
{
for(int i = 0;i < m;i++) scanf("%s",s[i]);
while (!isalpha(ss = getchar()));
memset(map,255,sizeof(map));
tot = 0;
for(int i = 0;i < m;i++)
for(int j = 0;j < n;j++)
map[i+1][j+1] = fff(s[i][j],i+1,j+1);
tot = (1 << tot) - 1;
knight();
memset(f,255,sizeof(f));
tt = l = 0;
list[tt] = bianma(te = ax*1000+ay*100+bx*10+by-1111,0,0);
f[te][0][0] = 0;
flag = -1;
while (tt >= l)
{
jiema(list[l]);
l++;
if (tb - ta != 10){
if ((map[bx-1][by] != -1)&&(tb - ta != 20)&&(tb - tk[t1] != 10))
work(ax+1,ay,bx-1,by,p);
else work(ax+1,ay,bx,by,p);}
if (ta - tb != 10){
if ((map[bx+1][by] != -1)&&(ta - tb != 20)&&(tk[t1] - tb != 10))
work(ax-1,ay,bx+1,by,p);
else work(ax-1,ay,bx,by,p);}
if (tb - ta != 1){
if ((map[bx][by-1] != -1)&&(tb - ta != 2)&&(tb - tk[t1] != 1))
work(ax,ay+1,bx,by-1,p);
else work(ax,ay+1,bx,by,p);}
if (ta - tb != 1){
if ((map[bx][by+1] != -1)&&(ta - tb != 2)&&(tk[t1] - tb != 1))
work(ax,ay-1,bx,by+1,p);
else work(ax,ay-1,bx,by,p);}
}
printf("%d\n",flag);
//if (flag > 0) print(last);
}
int main()
{
while(scanf("%d%d",&m,&n) != EOF) begin();
return 0;
}
}}}
BFS
题意是给一张地图(最大10*10),上面有两个出口,一些石块,一些记忆碎片(不超过5个),三颗棋子(爱丽丝,镜像,骑士)。
棋子均不能出围墙
骑士面朝给定的方向每回合向前移动一步,如果不能向前移动,则转身(原地不动,消耗一回合)
爱丽丝每回合可向上下左右移动一步,且必须移动
镜像与爱丽丝的移动方向相反,移动一步(如果前方是棋子或石块或围墙则不移动)
只有爱丽丝可以捡记忆碎片
每回合移动顺序:爱丽丝 镜像 骑士
目标:爱丽丝捡起所有记忆碎片之后与镜像同一回合在出口上
注意:
1.爱丽丝与镜像均在出口上时迷宫解除,这一回合骑士来不及动
2.爱丽丝与镜像均在出口上时迷宫解除,不能继续捡记忆碎片
本题的解法为BFS,状态数最多为10*10(爱丽丝的位置)*10*10(镜像的位置)*20(骑士的位置,最多有20种状态)*32(用二进制存储记忆碎片获得情况)=6400000
由于状态的总量较大,且组成状态的变量多,为了节省内存,可以将其压缩成一个变量
by luyi
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int O = 31;
char s[20][20],ss;
int map[20][20],f[10000][20][32],list[6400000],kx1[30],ky1[30],tk[30],len;
int m,n,tot,l,tt,kx,ky,ax,ay,bx,by,p,kk,dx,dy,t1,temp,te,flag,ta,tb;
//int pre[6400000],pret,last;
int bianma(int te,int k,int p)
{
return ((te+k*10000) << 5) + p;
}
void jiema(int k)
{
p = k & O;
k = k >> 5;
kk = f[te = (k % 10000)][temp = (k/10000)][p]+1;
tb = te % 100;
ta = te / 100;
by = tb % 10 + 1;
bx = tb / 10 + 1;
ay = ta % 10 + 1;
ax = ta / 10 + 1;
t1 = temp;
if ((++temp) == len) temp = 0;
kx = kx1[temp];
ky = ky1[temp];
}
void fangxiang(char s)
{
dx = dy = 0;
if (s == 'E') dy = 1; else
if (s == 'W') dy = -1; else
if (s == 'N') dx = -1; else
if (s == 'S') dx = 1;
}
int fff(char s,int x,int y)
{
if (s == '*') return 0;
if (s == 'R') return -1;
if (s == 'K') {kx = x;ky = y;return 0;}
if (s == 'A') {ax = x;ay = y;return 0;}
if (s == 'B') {bx = x;by = y;return 0;}
if (s == 'E') return -2;
if (s == 'M') {tot++;return (1<<(tot-1));}
}
void step()
{
kx1[len] = kx;
ky1[len++] = ky;
kx += dx;
ky += dy;
}
void walk()
{
while (map[kx][ky] != -1) step();
kx -= dx;
ky -= dy;
dx = -dx;
dy = -dy;
}
void knight()
{
kx1[0] = kx;
ky1[0] = ky;
len = 1;
fangxiang(ss);
kx += dx;
ky += dy;
walk();
walk();
while (!((kx == kx1[0])&&(ky == ky1[0]))) step();
for(int i = 0;i < len;i++) tk[i] = 10*kx1[i]+ky1[i]-11;
}
void work(int ax,int ay,int bx,int by,int p)
{
//pret = l-1;
if ((ax == bx)&&(ay == by)) return;
if (map[ax][ay] == -1) return;
if (map[ax][ay] > 0) p = (p | map[ax][ay]);
te = ax*1000+ay*100+bx*10+by - 1111;
if (f[te][temp][p] >= 0) return;
if ((ax == kx1[t1])&&(ay == ky1[t1])) return;
if ((bx == kx1[t1])&&(by == ky1[t1])) return;
if ((map[ax][ay] == -2)&&(map[bx][by] == -2))
{
if (p == tot)
{
flag = kk;
//last = tt+1;
l = tt + 1000;
} else
{
f[te][temp][p] = 0;
return;
}
}
if ((ax == kx)&&(ay == ky)) return;
if ((bx == kx)&&(by == ky)) return;
list[++tt] = bianma(te,temp,p);
//pre[tt] = pret;
f[te][temp][p] = kk;
}
//void print(int pret)
//{
// if (pret > 0) print(pre[pret]);
// jiema(list[pret]);
// printf("%d %d %d %d %d %d\n",ax,ay,bx,by,kx1[t1],ky1[t1]);
//}
void begin()
{
for(int i = 0;i < m;i++) scanf("%s",s[i]);
while (!isalpha(ss = getchar()));
memset(map,255,sizeof(map));
tot = 0;
for(int i = 0;i < m;i++)
for(int j = 0;j < n;j++)
map[i+1][j+1] = fff(s[i][j],i+1,j+1);
tot = (1 << tot) - 1;
knight();
memset(f,255,sizeof(f));
tt = l = 0;
list[tt] = bianma(te = ax*1000+ay*100+bx*10+by-1111,0,0);
f[te][0][0] = 0;
flag = -1;
while (tt >= l)
{
jiema(list[l]);
l++;
if (tb - ta != 10){
if ((map[bx-1][by] != -1)&&(tb - ta != 20)&&(tb - tk[t1] != 10))
work(ax+1,ay,bx-1,by,p);
else work(ax+1,ay,bx,by,p);}
if (ta - tb != 10){
if ((map[bx+1][by] != -1)&&(ta - tb != 20)&&(tk[t1] - tb != 10))
work(ax-1,ay,bx+1,by,p);
else work(ax-1,ay,bx,by,p);}
if (tb - ta != 1){
if ((map[bx][by-1] != -1)&&(tb - ta != 2)&&(tb - tk[t1] != 1))
work(ax,ay+1,bx,by-1,p);
else work(ax,ay+1,bx,by,p);}
if (ta - tb != 1){
if ((map[bx][by+1] != -1)&&(ta - tb != 2)&&(tk[t1] - tb != 1))
work(ax,ay-1,bx,by+1,p);
else work(ax,ay-1,bx,by,p);}
}
printf("%d\n",flag);
//if (flag > 0) print(last);
}
int main()
{
while(scanf("%d%d",&m,&n) != EOF) begin();
return 0;
}