• 程序设计思维与实践 Week2 作业 (3/4/数据班)


    下面的两道题的from,其实就是常用的pre数组 --编辑于2020.06.25

    A-迷宫


    问题描述:

     5× 5的二维数组,仅由0、1两数字组成,表示迷宫,0表示可以走,1不可以走,要求找出一条从左上角到右下角的路径,并输出路径

    思路:

    典型的搜索问题,这里可以用BFS实现,主要的困难是如何记录路径并输出,要记录路径至少实现两点:储存点和存储点之间的关系,最初的想法是在结构体中加上pre和num两个变量来再加上一个数组存储到过的点,num表示点在数组中的位置,pre表示其前驱,后来受到第二题的启发,发现map可以既存储路径,还能储存关系,最后用map实现的路径输出,同样,map也可以代替visited数组判断是否访问过,但是,map查找的复杂度是log,而数组查找的复杂度是O(1),相比较来说,数组更省时间,但是代码量多。用unorder_map?更好?

    总结:

    输出路径只要记录点和点之间的关系即可,map可实现状态到状态的转移,从而实现递归输出,用数组可以模拟此过程。

    代码:

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <queue> 
     4 #include <map>
     5 using namespace std;
     6 
     7 struct point
     8 {
     9     int x,y;
    10     bool operator < (const point &a) const
    11     {
    12         if(x!=a.x) return x<a.x;
    13         else return y<a.y;
    14     }
    15 };
    16 map<point,point> from;   //建立从点到点的映射,来输出路径
    17  
    18 int dx[]={-1,0,1,0};
    19 int dy[]={0,1,0,-1}; 
    20 
    21 queue<point> q;
    22 int maze[10][10],visited[10][10];
    23 int sx=0,sy=0,tx=4,ty=4;
    24 void output(point &t)
    25 {
    26     if(t.x==0&&t.y==0)
    27     {
    28         printf("(0, 0)
    ");
    29         return;
    30     }
    31     output( from[t] );
    32     printf("(%d, %d)
    ",t.x,t.y);
    33 }
    34 int main()
    35 {
    36     int n=5;
    37     for(int i=0;i<5;i++)
    38         for(int j=0;j<5;j++)
    39             scanf("%d",&maze[i][j]);
    40     q.push( {sx,sy} );
    41     visited[sx][sy]=1;
    42     while( !q.empty() )
    43     {
    44         point now=q.front();
    45         q.pop();
    46         for(int i=0;i<4;i++)
    47         {
    48             int x=dx[i]+now.x, 
    49                 y=dy[i]+now.y;
    50             if(x>=0&&x<=4&&y>=0&&y<=4&&
    51                 visited[x][y]==0&&maze[x][y]!=1)  //visited可以用map替代但前者时间更优
    52             {
    53                 point t={x,y};
    54                 from[ t ] =now;
    55                 q.push( t );
    56                 visited[x][y]=1;
    57                 if(x==4&&y==4)
    58                 {
    59                     output( t);
    60                     return 0;
    61                 }
    62             }
    63         }
    64     }
    65     return 0;
    66 }

    B-倒水

    问题描述:

    两个容量为A、B的杯子,有无尽的水可以浪费,要求经过下列过程使某个杯子里的水是C单位: "fill A" 表示倒满A杯,"empty A"表示倒空A杯,"pour A B" 表示把A的水倒到B杯并且把B杯倒满或A倒空。要求输出倒水过程。

    思路:

    为什么叫隐式图问题?我的理解是:所有能够到达的状态是事先不知道的,到达一个状态之后才能确定下一个状态。如何扩展队列里的状态?对A、B杯进行6种操作,每次操作都能得到一种新的状态,入队,继续搜索,如此BFS。关于倒水过程的输出,和上一题思路一样,map+递归。这里有一个点,是我刚开始没注意到,感觉没问题,提交WA,后来打算明天再说,晚上洗刷的时候突然想起来的,在结构体status中有变量num,表示由哪种操作得到这个状态,因为map用于用户自定义类型时,要重载<,起初我这样写

     1 struct status
     2 {
     3     int a,b;
     4     int num;   //当前操作的编号 
     5     bool operator <(const status &x) const 
     6     {
     7         if(a!=x.a) return a<x.a;
     8         else if(b!=x.b) return b<x.b;
     9         else return num<x.num;
    10     }
    11 };

    后来想到第9行,不能写,因为无论什么操作得来的,只要ab相同,状态就是相同,把第九行加了注释,再提交就AC了。还有一个问题是,最初我在refresh中判断是否到达了C,但是我发现这样的话,while循环不能及时停止,又把特判移到了while中,但是前者比后者能更早结束,传参的话太麻烦,有无更好的解决办法?暂时没想到

    总结:隐式图BFS,按照规则从最初状态扩展状态,map+递归输出

    代码:

    #include <cstdio>
    #include <iostream>
    #include <map>
    #include <queue>
    using namespace std;
    char step[10][20]={"","empty A","empty B","fill A","fill B","pour A B","pour B A"};
    int a,b,c;
    
    struct status
    {
        int a,b;
        int num;   //当前操作的编号 
        bool operator <(const status &x) const 
        {
            if(a!=x.a) return a<x.a;
            else if(b!=x.b) return b<x.b;
            //else return num<x.num;
        }
    };
    map<status,status> from;
    queue<status> q;
    void output(status t)
    {
        if( from.find(t)==from.end() || (t.a==0&&t.b==0) )
        {
            return; //回溯到了最初状态 
        }
        output( from[t] );
        printf("%s
    ",step[ t.num ]);
    } 
    void refresh(status s,status t)
    {
        if( from.find(t)==from.end() )    //状态t没有被访问到过 
        {
            from[t]=s;    //状态t由状态s生成 
            q.push(t);
        }
    }
    void bfs()
    {
        status t={0,0,0};
        q.push( t );
    //    visited[a][b]=1; //可以直接用map判断是否访问过,但是不如visited的时间复杂度好 
        while( !q.empty() )
        {
            status now=q.front();
            q.pop();
            if(now.a==c||now.b==c)    //now=当前状态,tar=target目标状态
            {
                output(now);
                cout<<"success"<<endl;
                return ;
            }
            //判断可能的每一步
            status tar;
            if(now.a>0)    //empty A
            {
                tar={0,now.b,1};
                refresh(now,tar);
            }
            if(now.b>0)    //empty B
            {
                tar={now.a,0,2};
                refresh(now,tar);
            }
            if(now.a<a)    //fill A
            {
                tar={a,now.b,3};
                refresh(now,tar);
            }    
            if(now.b<b)    //fill B
            {
                tar={now.a,b,4};
                refresh(now,tar); 
            }
            if(now.a>0)  // pour A B
            {
                if(now.a+now.b<=b)  //把A倒空
                {
                    tar={0,now.a+now.b,5};
                    refresh(now,tar);
                }
                else                //把B倒满 
                {
                    tar={now.a-(b-now.b),b,5};
                    refresh(now,tar);
                }
            }
            if(now.b>0)    //pour B A
            {
                if(now.b+now.a<=a)    //把B倒空
                {
                    tar={now.a+now.b,0,6};
                    refresh(now,tar);
                } 
                else                //把A倒满 
                {
                    tar={a,now.b-(a-now.a),6};
                    refresh(now,tar);
                }
            }
        }
    }
    int main()
    {
        //freopen("a.in","r",stdin);
        while( scanf("%d %d %d",&a,&b,&c)==3 )
            bfs();
    }
  • 相关阅读:
    NOI AC#62 color(树上操作)
    fenbi
    bert 压缩优化方向的论文
    bert 编程入门| bert 瘦身技巧
    行政法+刑法+民法
    Bert原理 | Bert油管视频学习法
    vscode的使用以及快捷键总结
    NG课程笔记学习记录
    古典文学+古曲+四大文明古国
    中国地理+地球上的水和大气
  • 原文地址:https://www.cnblogs.com/qingoba/p/12400946.html
Copyright © 2020-2023  润新知