• POJ


    题目链接

    有一个n*m(1<=n,m<=20)的网格图,图中有k堵墙和有一条长度为L(L<=8)的蛇,蛇在移动的过程中不能碰到自己的身体。求蛇移动到点(1,1)所需的最小步数。

    显然用8个(x,y)来表示蛇的状态是不现实的(用哈希也很难存下,要么爆内存,要么超时),所以首先应当进行状态压缩。可以发现蛇的身体是连续的,因此可以用一个表示方向的向量来储存蛇身体的每个部分在它上个部分的哪个方向(只有头部用(x,y)表示),这样总状态数就变成了n*m*(2^((L-1)*2)),在可接受范围内了。

    可优化的部分:

    1.可以在图的边界上放上一层墙,避免出界判定。

    2.如果在碰撞判定时把“蛇身”和“墙”等同的话,为了防止在撤销蛇身所造成的影响的同时把墙也一并消除,可以用b[x][y]++,--的方法来代替b[x][y]=1。

    AC代码:(编码和解码真是个体力活)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 using namespace std;
     5 typedef long long ll;
     6 const int N=20+2;
     7 const int dx[]= {0,0,-1,1};
     8 const int dy[]= {-1,1,0,0};
     9 struct P {int x,y;} p[N],r[N];
    10 int n,m,l,k,b[N][N],d[N][N][(1<<12)+10],S,ka;
    11 struct D {int x,y,S;};
    12 
    13 int bfs() {
    14     queue<D> q;
    15     memset(d,-1,sizeof d);
    16     d[p[0].x][p[0].y][S]=0,q.push({p[0].x,p[0].y,S});
    17     while(!q.empty()) {
    18         int x=q.front().x,y=q.front().y,S=q.front().S;
    19         q.pop();
    20         if(x==1&&y==1)return d[x][y][S];
    21         r[0]= {x,y};
    22         for(int i=1; i<l; ++i) {
    23             int t=S>>((l-i-1)*2)&3;
    24             r[i]= {r[i-1].x+dx[t],r[i-1].y+dy[t]};
    25         }
    26         for(int i=0; i<l; ++i)b[r[i].x][r[i].y]++;
    27         for(int i=0; i<4; ++i) {
    28             int xx=x+dx[i],yy=y+dy[i],SS=S>>2|((i^1)<<((l-2)*2));
    29             if(!b[xx][yy]&&!~d[xx][yy][SS])d[xx][yy][SS]=d[x][y][S]+1,q.push({xx,yy,SS});
    30         }
    31         for(int i=0; i<l; ++i)b[r[i].x][r[i].y]--;
    32     }
    33     return -1;
    34 }
    35 
    36 int main() {
    37     while(scanf("%d%d%d",&n,&m,&l)&&n) {
    38         printf("Case %d: ",++ka);
    39         for(int i=0; i<l; ++i)scanf("%d%d",&p[i].x,&p[i].y);
    40         S=0;
    41         for(int i=1; i<l; ++i)
    42             for(int j=0; j<4; ++j)if(p[i-1].x+dx[j]==p[i].x&&p[i-1].y+dy[j]==p[i].y) {
    43                     S=S<<2|j;
    44                     break;
    45                 }
    46         memset(b,0,sizeof b);
    47         for(int i=0; i<=n+1; ++i)b[i][0]=b[i][m+1]=1;
    48         for(int i=0; i<=m+1; ++i)b[0][i]=b[n+1][i]=1;
    49         scanf("%d",&k);
    50         while(k--) {
    51             int x,y;
    52             scanf("%d%d",&x,&y);
    53             b[x][y]=1;
    54         }
    55         printf("%d
    ",bfs());
    56     }
    57     return 0;
    58 }
    bfs

    也可以进一步用A*优化,以每个点到点(1,1)的距离为估价函数进行转移,速度大幅提升。

    一般A*中优先队列的结点要额外设一个属性g来表示已走过的距离,但也可以省去这个g,用d[x][y][S]来代替g,这样如果要把bfs改成A*的话,只要写出求每个点到点(1,1)距离的bfs代码,然后把原来的bfs代码中的queue换成priority_queue,front换成top就ok了,非常方便。

    另外可以优化的一个地方是如果一个点不能到达(1,1),那么直接返回-1就行,不用再浪费时间跑A*了。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 using namespace std;
     5 typedef long long ll;
     6 const int N=20+2;
     7 const int dx[]= {0,0,-1,1};
     8 const int dy[]= {-1,1,0,0};
     9 struct P {int x,y;} p[N],r[N];
    10 int n,m,l,k,b[N][N],d[N][N][(1<<12)+10],S,h[N][N],ka;
    11 struct D {
    12     int x,y,S;
    13     bool operator<(const D& b)const {return d[x][y][S]+h[x][y]>d[b.x][b.y][b.S]+h[b.x][b.y];}
    14 };
    15 
    16 void bfs() {
    17     queue<P> q;
    18     memset(h,-1,sizeof h);
    19     h[1][1]=0,q.push({1,1});
    20     while(!q.empty()) {
    21         int x=q.front().x,y=q.front().y;
    22         q.pop();
    23         for(int i=0; i<4; ++i) {
    24             int xx=x+dx[i],yy=y+dy[i];
    25             if(!~h[xx][yy]&&!b[xx][yy])h[xx][yy]=h[x][y]+1,q.push({xx,yy});
    26         }
    27     }
    28 }
    29 
    30 int Astar() {
    31     if(!~h[p[0].x][p[0].y])return -1;
    32     priority_queue<D> q;
    33     memset(d,-1,sizeof d);
    34     d[p[0].x][p[0].y][S]=0,q.push({p[0].x,p[0].y,S});
    35     while(!q.empty()) {
    36         int x=q.top().x,y=q.top().y,S=q.top().S;
    37         q.pop();
    38         if(x==1&&y==1)return d[x][y][S];
    39         r[0]= {x,y};
    40         for(int i=1; i<l; ++i) {
    41             int t=S>>((l-i-1)*2)&3;
    42             r[i]= {r[i-1].x+dx[t],r[i-1].y+dy[t]};
    43         }
    44         for(int i=0; i<l; ++i)b[r[i].x][r[i].y]++;
    45         for(int i=0; i<4; ++i) {
    46             int xx=x+dx[i],yy=y+dy[i],SS=S>>2|((i^1)<<((l-2)*2));
    47             if(!b[xx][yy]&&!~d[xx][yy][SS])d[xx][yy][SS]=d[x][y][S]+1,q.push({xx,yy,SS});
    48         }
    49         for(int i=0; i<l; ++i)b[r[i].x][r[i].y]--;
    50     }
    51     return -1;
    52 }
    53 
    54 int main() {
    55     while(scanf("%d%d%d",&n,&m,&l)&&n) {
    56         printf("Case %d: ",++ka);
    57         for(int i=0; i<l; ++i)scanf("%d%d",&p[i].x,&p[i].y);
    58         S=0;
    59         for(int i=1; i<l; ++i)
    60             for(int j=0; j<4; ++j)if(p[i-1].x+dx[j]==p[i].x&&p[i-1].y+dy[j]==p[i].y) {
    61                     S=S<<2|j;
    62                     break;
    63                 }
    64         memset(b,0,sizeof b);
    65         for(int i=0; i<=n+1; ++i)b[i][0]=b[i][m+1]=1;
    66         for(int i=0; i<=m+1; ++i)b[0][i]=b[n+1][i]=1;
    67         scanf("%d",&k);
    68         while(k--) {
    69             int x,y;
    70             scanf("%d%d",&x,&y);
    71             b[x][y]=1;
    72         }
    73         bfs();
    74         printf("%d
    ",Astar());
    75     }
    76     return 0;
    77 }
    A*
  • 相关阅读:
    termux 查看硬件信息
    从专业技能、行业知识和软实力的人才三角谈起程序员
    go代理
    flameshot 截图工具
    firefox重定向插件
    Firefox扩展开发 为扩展添加图标
    fabric 区块/账本解析工具 blocklator
    Linux安装openjdk
    【SQLServer】使用DMVs查找慢查询
    【SQLServer】SQLServer内存使用查看
  • 原文地址:https://www.cnblogs.com/asdfsag/p/10364238.html
Copyright © 2020-2023  润新知