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

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)\n");
29         return;
30     }
31     output( from[t] );
32     printf("(%d, %d)\n",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\n",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();
}

猜你喜欢

转载自www.cnblogs.com/qingoba/p/12400946.html
今日推荐