程序设计思维与实践 Week2 作业

A - Maze

题意:
根据一张5×5的地图,起点为左上角,终点为右下角,输出最短路径。数据保证有唯一解。
思路:
这题是典型的迷宫问题。使用结构体数组记录每个点,利用bfs广度优先搜索,pre二维数组记录该点之前的点。最后使用递归思想寻找出路径。
总结:

结构体数组+bfs+路径记录,顺利解决问题。

代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
struct Point
{
	int x, y;
	Point() {}
	Point(int _x, int _y) : x(_x), y(_y) {}
	bool operator==(const Point &t) const
	{
		return x == t.x && y == t.y;
	}
};
const int maxn = 5;
bool vis[maxn][maxn];
int a[maxn][maxn], dis[maxn][maxn], n, m;
int sx, sy, tx, ty;
queue<Point> Q;
int pre[5][5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
void findthepath(int x, int y)
{
	int px = pre[x][y] / 10, py = pre[x][y] % 10;
	if (px == 0 && py == 0)
	{
		cout << "(" << px << ", " << py << ")" << endl;
		cout << "(" << x << ", " << y << ")" << endl;
		return;
	}
	findthepath(px, py);
	cout << "(" << x << ", " << y << ")" << endl;
}
int bfs()
{
	Q.push(Point(sx, sy));
	memset(vis, 0, sizeof(vis));
	memset(dis, 0, sizeof(dis));
	vis[sx][sy] = true;
	dis[sx][sy] = 0;
	while (!Q.empty())
	{
		Point now = Q.front();
		Q.pop();
		for (int i = 0; i < 4; i++)
		{
			int x = now.x + dx[i], y = now.y + dy[i];
			if (x >= 0 && x <= n && y >= 0 && y <= m && !vis[x][y] && !a[x][y])
			{
				dis[x][y] = dis[now.x][now.y] + 1;
				pre[x][y] = now.x * 10 + now.y;//哈希方法
				if (x == tx && y == ty)
					return dis[tx][ty];
				vis[x][y] = 1;
				Q.push(Point(x, y));
			}
		}
	}
	return dis[tx][ty];
}
int main()
{
	sx = sy = 0;
	tx = ty = m = n = 4;
	for (int i = 0; i <= 4; i++)
		for (int j = 0; j <= 4; j++)
			cin >> a[i][j];
	bfs();
	findthepath(tx, ty);
	system("pause");
}

B - Pour Water

题意:
倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。

  • Input
    输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
  • Output
    你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。

思路:
和经典的倒水问题有些区别,这里要求输出的不是中间结果,而是中间动作。这里有两种解决思路:

  1. 自设结构体,列出所有的状态,进行bfs,记录所有的中间态。
  2. 第一种方法的缺点就是需要列出所有状态,记录中间态也不方便,丧失了map的搜索元素,添加新元素等操作的便捷性。因此我在使用map<status,status>的基础上,新增了一个map<status,string>,用来记录中间倒水动作。每一次refresh的过程中,记录这个状态映射的动作。最后输出的时候,先根据map<status,status>找到上一个状态,再根据map<status,string>找到这个状态对应的倒水动作。这样的作法,基本没有改变经典倒水问题的代码,保留了map的高效。

总结:
经典倒水问题的思路就是使用map,利用bfs记录状态是否被访问过了。自认为新增map<status,string>记录中间动作是一种还不错的方法。
代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <map>
#include <cstring>
using namespace std;

struct Status
{
    int a, b;
    bool operator<(const Status &s) const
    {
        return a != s.a ? a < s.a : b < s.b;
    }
};
queue<Status> Q;
map<Status, Status> from;
map<Status, string> pre;
/* 递归输出方案 */
void print(Status &p)
{
    if (from.find(p) == from.end() || (p.a == 0 && p.b == 0))
        return;
    print(from[p]); // 递归
    //printf("-><%d,%d>", p.a, p.b);
    cout << pre[p].c_str() << endl;
}

void refresh(Status &s, Status &t, string ch)
{
    if (from.find(t) == from.end())
    { // 特判合法,加入队列
        from[t] = s;
        pre[t] = ch;
        Q.push(t);
    }
}

bool bfs(int A, int B, int C)
{
    // 起点, 两杯水都空
    Status s, t;
    s.a = 0;
    s.b = 0;
    Q.push(s);

    while (!Q.empty())
    {
        // 取队首
        s = Q.front();
        Q.pop();
        // 特判到达终点
        if (s.a == C || s.b == C)
        {
            print(s); // 输出方案
            return 1;
        }

        // 倒空 a 杯的水
        if (s.a > 0)
        {
            t.a = 0;   // 倒空
            t.b = s.b; // b 杯不变
            refresh(s, t, "empty A");
        }

        // 同理,倒空 b 杯的水
        if (s.b > 0)
        {
            t.b = 0;   // 倒空
            t.a = s.a; // a 杯不变
            refresh(s, t, "empty B");
        }

        // a 杯未满,续满 a 杯
        if (s.a < A)
        {
            // 续满 a 杯
            t.a = A;
            t.b = s.b;
            refresh(s, t, "fill A");

            // 考虑倒入
            if (s.b != 0)
            {
                if (s.a + s.b <= A)
                {
                    t.a = s.a + s.b;
                    t.b = 0;
                    refresh(s, t, "pour B A");
                }
                else
                {
                    t.a = A;
                    t.b = s.a + s.b - A;
                    refresh(s, t, "pour B A");
                }
            }
        }

        // 同理,b 杯未满,续满 b 杯
        if (s.b < B)
        {
            t.a = s.a;
            t.b = B;
            refresh(s, t, "fill B");
            if (s.a != 0)
            {
                if (s.a + s.b <= B)
                {
                    t.a = 0;
                    t.b = s.a + s.b;
                    refresh(s, t, "pour A B");
                }
                else
                {
                    t.a = s.a + s.b - B;
                    t.b = B;
                    refresh(s, t, "pour A B");
                }
            }
        }
    }
    return -1;
}
int main()
{
    int a, b, c;
    while (scanf("%d%d%d", &a, &b, &c) == 3)
    {
        from.erase(from.begin(), from.end());
        pre.erase(pre.begin(), pre.end());
        while (!Q.empty())
            Q.pop();
        int flag = bfs(a, b, c);
        if (flag == 1)
            cout << "success" << endl;
    }
    //system("pause");
    return 0;
} /* 26 29 11 */
发布了24 篇原创文章 · 获赞 1 · 访问量 1655

猜你喜欢

转载自blog.csdn.net/lllsy_12138/article/details/104631138