Tag Archives: BFS

LeetCode Serialize and Deserialize Binary Tree

297. Serialize and Deserialize Binary Tree

Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.

Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.

Example: 

You may serialize the following tree:

    1
   / \
  2   3
     / \
    4   5

as "[1,2,3,null,null,4,5]"

Clarification: The above format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.

Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.


序列化一棵二叉树。要求把一棵二叉树转换为一个字符串,并且能根据这个字符串反序列化回原来的二叉树。
首先我们使用LeetCode Binary Tree Level Order Traversal层次遍历的方法得到二叉树的层次遍历序列,然后把数字转换为字符串并用逗号拼接起所有字符串,遇到空节点,则用空字符串表示。比如下面这棵树,序列化成字符串为:1,2,3,,,4,,,5。本来5后面还有空串的,即3的右孩子及其孩子,但是为了节省空间,后面的空串和逗号都省略了。

反序列化有点技巧。正常如果是一棵完全二叉树,如果下标从0开始,则节点i的左右孩子的下标分别为2*i+1和2*i+2,所以我们可以用递归的方法快速反序列化出来,就像我在LeetCode Minimum Depth of Binary Tree给出的根据数组构建树结构的genTree函数一样。对应到上图,完全二叉树的数组表示应该是{1,2,3,-1,-1,4,-1,-1,-1,-1,-1,-1,5,-1,-1},只有把这个数组传入genTree函数才能生成像上图的二叉树。
因为经过层次遍历之后的数组是{1,2,3,-1,-1,4,-1,-1,5},也就是说2后面只保留了其直接的两个空节点,并没有保留其4个空的孙子节点。导致4号节点(下标为5)在找孩子节点时,使用2*i+1=11和2*i+2=12找左右孩子的下标时,数组越界,导致漏掉了右孩子5。
后来参考了LeetCode官方的二叉树反序列化作者的介绍,恍然大悟。因为数组是层次遍历的结果,所以兄弟节点的孩子节点是按先后顺序存储在数组中的,我们可以分别维护一个父亲节点par和孩子节点child,一开始par=0,child=1,表示数组的0号节点为父亲节点,1号节点为左孩子节点,然后我们把child接到par的左孩子,把child+1接到par的右孩子,par++,child+=2,当遇到child为空,也要把空节点接到par上面。当遇到par为空时,直接跳过就好,但是child不移动。这样就能避免父亲为空,孩子节点的下标对不上的问题。比如上例中,par指向数值3时,左child指向数值4,右child指向-1;当par指向后面的两个-1时,child不动;当par指向4时,左child指向下一个-1,右child指向5正确。
完整代码如下:

class Codec {
public:
    string bfs(TreeNode* root)
    {
        if (root == NULL)
            return "";
        string tree = "";
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            TreeNode* t = q.front();
            q.pop();
            if (t == NULL) {
                tree += ",";
                continue;
            }
            tree += to_string(t->val) + ",";
            q.push(t->left);
            q.push(t->right);
        }
        return tree;
    }
    // Encodes a tree to a single string.
    string serialize(TreeNode* root)
    {
        string tree = bfs(root);
        if (tree == "")
            return "";
        int end = tree.size() – 1;
        while (tree[end] == ‘,’)
            –end;
        return tree.substr(0, end + 1);
    }
    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data)
    {
        if (data == "")
            return NULL;
        vector<TreeNode*> tree;
        for (int start = 0, end = 0; end <= data.size(); ++end) {
            if (data[end] == ‘,’ || end == data.size()) {
                string tmp = data.substr(start, end – start);
                if (tmp == "")
                    tree.push_back(NULL);
                else
                    tree.push_back(new TreeNode(atoi(tmp.c_str())));
                start = end + 1;
            }
        }
        int par = 0, child = 1;
        while (child < tree.size()) {
            if (tree[par]) {
                if (child < tree.size())
                    tree[par]->left = tree[child++];
                if (child < tree.size())
                    tree[par]->right = tree[child++];
            }
            ++par;
        }
        return tree[0];
    }
};

本代码提交AC,用时39MS。

二刷。逻辑性更好的代码:

class Codec {
public:

	// Encodes a tree to a single string.
	string serialize(TreeNode* root) {
		vector<string> nodes;
		queue<TreeNode*> q;
		q.push(root);
		while (!q.empty()) {
			int n = q.size();
			while (n--) {
				TreeNode* cur = q.front();
				q.pop();
				if (cur == NULL)nodes.push_back("null");
				else {
					nodes.push_back(to_string(cur->val));
					q.push(cur->left);
					q.push(cur->right);
				}
			}
		}
		string ans = "";
		int n = nodes.size();
		for (int i = 0; i < n - 1; ++i) {
			ans += nodes[i] + ",";
		}
		return ans + nodes[n - 1];
	}

	// Decodes your encoded data to tree.
	TreeNode* deserialize(string data) {
		vector<string> nodes;
		int n = data.size();
		int i = 0, j = 0;
		while (i < n) {
			j = i + 1;
			while (j < n&&data[j] != ',')++j;
			string cur = data.substr(i, j - i);
			nodes.push_back(cur);
			i = j + 1;
		}

		vector<TreeNode*> tnodes;
		for (int i = 0; i < nodes.size(); ++i) {
			if (nodes[i] == "null")tnodes.push_back(NULL);
			else tnodes.push_back(new TreeNode(atoi(nodes[i].c_str())));
		}
		
		int par = 0, child = 1;
		while (child < tnodes.size()) {
			if (tnodes[par] == NULL) {
				++par;
			}
			else {
				tnodes[par]->left = tnodes[child++];
				tnodes[par++]->right = tnodes[child++];
			}
		}
		return tnodes[0];
	}
};

本代码提交AC,用时72MS。

LeetCode Symmetric Tree

101. Symmetric Tree

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

For example, this binary tree [1,2,2,3,4,4,3] is symmetric:

    1
   / \
  2   2
 / \ / \
3  4 4  3

But the following [1,2,2,null,3,null,3] is not:

    1
   / \
  2   2
   \   \
   3    3

Follow up: Solve it both recursively and iteratively.


本题要判断一棵二叉树是否是镜面对称的,镜面对称就像题中第一个例子,在树中间画一条线,则树的左右两边是镜面对称的。
无论是递归还是迭代的解法,镜面对称的树都需要满足以下三个条件:

  1. 左右节点的值相等
  2. 左节点的左孩子等于右节点的右孩子
  3. 左节点的右孩子等于右节点的左孩子

根据以上的三条规则,可以很容易写出递归版本的代码:

class Solution {
public:
    bool work(TreeNode* left, TreeNode* right)
    {
        if (left == NULL && right == NULL)
            return true;
        if (left == NULL || right == NULL)
            return false;
        bool cond1 = left->val == right->val;
        bool cond2 = work(left->left, right->right);
        bool cond3 = work(left->right, right->left);
        return cond1 && cond2 && cond3;
    }
    bool isSymmetric(TreeNode* root)
    {
        if (root == NULL)
            return true;
        return work(root->left, root->right);
    }
};

本代码提交AC,用时6MS。
迭代的版本中,因为每层都需要判断左右是否镜面对称,所以可以用类似层次遍历的方法,也相当于广度优先遍历。因为是左右两棵子树,所以需要用到两个队列,在节点入队的时候需要注意,因为以上的第2,3个条件,所以如果对左节点的孩子入队顺序是先左后右,那么对右节点的孩子入队顺序就应该是先右后左,这样出队时的顺序才能和2,3条件一致。完整代码如下:

class Solution {
public:
    bool isSymmetric(TreeNode* root)
    {
        if (root == NULL)
            return true;
        queue<TreeNode *> left, right;
        left.push(root->left);
        right.push(root->right);
        while (!left.empty() && !right.empty()) {
            TreeNode *l = left.front(), *r = right.front();
            left.pop();
            right.pop();
            if (l == NULL && r == NULL)
                continue;
            if (l == NULL || r == NULL)
                return false;
            if (l->val != r->val)
                return false;
            left.push(l->left);
            left.push(l->right);
            right.push(r->right);
            right.push(r->left);
        }
        return true;
    }
};

本代码提交AC,用时3MS。

二刷。迭代版本,用一个双端队列也可以解决,就是麻烦一点:

class Solution {
public:
	bool isSymmetric(TreeNode* root) {
		if (root == NULL)return true;
		deque<TreeNode*> q; // deque支持下标访问,而queue不支持
		q.push_back(root);
		while (!q.empty()) {
			int n = q.size();
			int i = 0, j = n - 1;
			bool curans = true;
			while (i < j) {
				if (q[i] != NULL && q[j] != NULL && q[i]->val == q[j]->val) {
					++i;
					--j;
				}
				else if (q[i] == NULL && q[j] == NULL) {
					++i;
					--j;
				}
				else {
					curans = false;
					break;
				}
			}
			if (!curans)return false;
			while (n--) {
				TreeNode* cur = q.front();
				if (cur != NULL) {
					q.push_back(cur->left);
					q.push_back(cur->right);
				}
				q.pop_front();
			}
		}
		return true;
	}
};

本代码提交AC,用时8MS。

LeetCode Minimum Depth of Binary Tree

111. Minimum Depth of Binary Tree

Given a binary tree, find its minimum depth.

The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.

Note: A leaf is a node with no children.

Example:

Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

return its minimum depth = 2.


求一棵二叉树的最小深度,类似于LeetCode Maximum Depth of Binary Tree求最大深度,但是稍微难一点。 请注意,不能直接把求最大深度的大于号改为小于号:

class Solution {
public:
    int dfs(TreeNode* root, int nDepth)
    {
        if (root == NULL)
            return nDepth;
        int left = dfs(root->left, nDepth + 1);
        int right = dfs(root->right, nDepth + 1);
        return left < right ? left : right;
    }
    int minDepth(TreeNode* root) { return dfs(root, 0); }
};

因为如上图所示,当走到A点时,没有右孩子,导致int right = dfs(root->right, nDepth + 1);会得到A点的高度2,比左孩子的高度低,即最小高度为2了。但是最小高度显然是4。所以需要稍加修改,使得如果某个孩子为空时,不在该孩子深搜了,只有当两个孩子都为空时(即是叶子节点时),才得到一个合法的深度,去和其他深度做比较。

深度搜索的完整代码如下:

class Solution {
public:
    int dfs(TreeNode* root, int nDepth)
    {
        if (root == NULL)
            return nDepth;
        if (root->left == NULL && root->right == NULL)
            return nDepth + 1;
        int left = root->left == NULL ? INT_MAX : dfs(root->left, nDepth + 1);
        int right = root->right == NULL ? INT_MAX : dfs(root->right, nDepth + 1);
        return left < right ? left : right;
    }
    int minDepth(TreeNode* root) { return dfs(root, 0); }
};

本代码提交AC,用时9MS。
但是DFS必须走到所有叶子节点才能得到最小深度,而如果采用广度优先搜索BFS时,遇到的第一个叶子节点的深度就是最小深度了,就可以返回了。所以理论上,BFS的性能会更优。
BFS的完整代码如下:

struct NewTreeNode {
    TreeNode* tn;
    int depth;
};
class Solution {
public:
    int bfs(TreeNode* root)
    {
        queue<NewTreeNode*> qTree;
        NewTreeNode* ntn = new NewTreeNode;
        ntn->tn = root;
        ntn->depth = 0;
        qTree.push(ntn);
        NewTreeNode* top = new NewTreeNode;
        while (!qTree.empty()) {
            top = qTree.front();
            qTree.pop();
            if (top->tn == NULL)
                return top->depth;
            if (top->tn->left == NULL && top->tn->right == NULL)
                return top->depth + 1;
            if (top->tn->left != NULL) {
                NewTreeNode* tmp = new NewTreeNode;
                tmp->tn = top->tn->left;
                tmp->depth = top->depth + 1;
                qTree.push(tmp);
            }
            if (top->tn->right != NULL) {
                NewTreeNode* tmp = new NewTreeNode;
                tmp->tn = top->tn->right;
                tmp->depth = top->depth + 1;
                qTree.push(tmp);
            }
        }
        return -1;
    }
    int minDepth(TreeNode* root) { return bfs(root); }
};

本代码提交AC,用时也是9MS。
我看Leetcode上有很多关于树的题,为了方便测试,自己写了一个由数组构造树结构的函数,如下:

#include <vector>
using namespace std;
// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x)
        : val(x)
        , left(NULL)
        , right(NULL)
    {
    }
};
TreeNode* genTree(vector<int>& vi, int id)
{
    if (vi.size() == 0)
        return NULL;
    TreeNode* root = new TreeNode(vi[id]);
    if (2 * id + 1 < vi.size() && vi[2 * id + 1] != -1) {
        root->left = genTree(vi, 2 * id + 1);
    }
    if (2 * id + 2 < vi.size() && vi[2 * id + 2] != -1) {
        root->right = genTree(vi, 2 * id + 2);
    }
    return root;
}
int main()
{
    vector<int> vTree = { 1, 2, 3, 4, 5 };
    TreeNode* root = genTree(vTree, 0);
    return 0;
}

使用时只要传入数组和0,返回的就是一个建好的树。用数组存树,一般第i号节点的左右孩子下标分别为2i+1和2i+2。存好之后恰好是一棵树的层次遍历结果。
如果要构建的不是一棵完全树,比如下面的左图,则先需要转换为完全图,也就是用-1代替一个不存在的节点,此时传入的数组应该是[1,-1,2,-1,-1,3,-1],genTree函数遇到-1会自动跳过不创建节点。当然如果你的树中本来就有-1这个值,也可以把-1换成其他数。

二刷。BFS的代码还可以写得更简洁漂亮一些,类似于层次遍历,遍历到某一层有叶子节点时,层数就是最小深度,代码如下:

class Solution {
public:
    int minDepth(TreeNode* root)
    {
        if (root == NULL)
            return 0;
        int depth = 0;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            ++depth;
            int n = q.size();
            while (n--) {
                TreeNode* cur = q.front();
                q.pop();
                if (cur->left == NULL && cur->right == NULL)
                    return depth;
                if (cur->left != NULL)
                    q.push(cur->left);
                if (cur->right != NULL)
                    q.push(cur->right);
            }
        }
        return depth;
    }
};

本代码提交AC,用时9MS。
DFS的代码也可以写得更漂亮一些:

class Solution {
public:
    int minDepth(TreeNode* root)
    {
        if (root == NULL)
            return 0;
        if (root->left == NULL)
            return minDepth(root->right) + 1;
        if (root->right == NULL)
            return minDepth(root->left) + 1;
        return min(minDepth(root->left), minDepth(root->right)) + 1;
    }
};

本代码提交AC,用时13MS,比BFS慢。

hihoCoder 1139-二分·二分答案

hihoCoder 1139-二分·二分答案 #1139 : 二分·二分答案 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在上一回和上上回里我们知道Nettle在玩《艦これ》,Nettle在整理好舰队之后终于准备出海捞船和敌军交战了。 在这个游戏里面,海域是N个战略点(编号1..N)组成,如下图所示 其中红色的点表示有敌人驻扎,猫头像的的点表示该地图敌军主力舰队(boss)的驻扎点,虚线表示各个战略点之间的航线(无向边)。 在游戏中要从一个战略点到相邻战略点需要满足一定的条件,即需要舰队的索敌值大于等于这两点之间航线的索敌值需求。 由于提高索敌值需要将攻击机、轰炸机换成侦察机,舰队索敌值越高,也就意味着舰队的战力越低。 另外在每一个战略点会发生一次战斗,需要消耗1/K的燃料和子弹。必须在燃料和子弹未用完的情况下进入boss点才能与boss进行战斗,所以舰队最多只能走过K条航路。 现在Nettle想要以最高的战力来进攻boss点,所以他希望能够找出一条从起始点(编号为1的点)到boss点的航路,使得舰队需要达到的索敌值最低,并且有剩余的燃料和子弹。 特别说明:两个战略点之间可能不止一条航线,两个相邻战略点之间可能不止一条航线。保证至少存在一条路径能在燃料子弹用完前到达boss点。 提示:你在找什么? 输入 第1行:4个整数N,M,K,T。N表示战略点数量,M表示航线数量,K表示最多能经过的航路,T表示boss点编号, 1≤N,K≤10,000, N≤M≤100,000 第2..M+1行:3个整数u,v,w,表示战略点u,v之间存在航路,w表示该航路需求的索敌值,1≤w≤1,000,000。 输出 第1行:一个整数,表示舰队需要的最小索敌值。 样例输入 5 6 2 5 1 2 3 1 3 2 1 4 4 2 5 2 3 5 5 4 5 3 样例输出 3


本题的目标是在无向图中,求从点1到T的路径中的索敌值的最大值的最小值,且经过的路径不能超过k段。 在输入的时候,我们能得到所有索敌值的最大值high和最小值low,high值显然能够成功到达T点,且不超过k段,因为题目说了保证至少存在一条路径能在燃料子弹用完前到达boss点;low就不一定了。所以二分答案的意思就是在区间[low,high]中二分查找最小可行解。 假设函数f(x)能判断当索敌值为x时,是否能成功到达T点且不超过k段。则每二分一次就求一次f(mid),根据f(mid)的值调整区间[low,high]的端点。所以现在的关键就是函数f(x)。 f(x)可以使用广度优先搜索实现。实现的时候注意不能使用邻接矩阵保存图,有可能TLE或MLE,使用邻接表更好。完整代码如下: [cpp] #include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> #include<queue> #include<vector> using namespace std; const int kMaxN = 10005, kINF = 0x3f, kMaxW = 1000005; vector<vector<int>> path; vector<vector<int>> powers; vector<bool> visit; int n, m, k, t; bool BFS(int power) { visit.clear(); visit.resize(n + 1, false); queue<int> Q; int u, v, len = 0;; Q.push(1); visit[1] = true; Q.push(-1); while (!Q.empty()&&len<k) { u = Q.front(); Q.pop(); if (u == -1) { if (Q.empty()) return false; else { len++; Q.push(-1); continue; } } for (int i = 0; i < path[u].size(); i++) { v = path[u][i]; if (!visit[v]&&powers[u][i] <= power) { if (v == t) return true; visit[v] = true; Q.push(v); } } } return false; } int main() { freopen("input.txt", "r", stdin); scanf("%d %d %d %d", &n, &m, &k, &t); path.resize(n + 1); powers.resize(n + 1); int u, v, w, low = kMaxW, high = 0; for (int i = 0; i < m; i++) { scanf("%d %d %d", &u, &v, &w); low = min(low, w); high = max(high, w); path[u].push_back(v); path[v].push_back(u); powers[u].push_back(w); powers[v].push_back(w); } while (low+1 < high) { int mid = (low + high) / 2; if (BFS(mid)) high = mid; else low = mid; } printf("%d\n", high); return 0; } [/cpp] 在BFS的时候可以单独保存每个点及该点此时的深度(len),也可以共用一个len,只需额外加入一个标记-1,当碰到-1时,说明已经遍历完一层了,深度加1。 本代码提交AC,用时189MS,内存8MB。]]>

hihoCoder 1136-Professor Q's Software

hihoCoder 1136-Professor Q’s Software #1136 : Professor Q’s Software 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Professor Q develops a new software. The software consists of N modules which are numbered from 1 to N. The i-th module will be started up by signal Si. If signal Si is generated multiple times, the i-th module will also be started multiple times. Two different modules may be started up by the same signal. During its lifecircle, the i-th module will generate Ki signals: E1, E2, …, EKi. These signals may start up other modules and so on. Fortunately the software is so carefully designed that there is no loop in the starting chain of modules, which means eventually all the modules will be stoped. Professor Q generates some initial signals and want to know how many times each module is started. 输入 The first line contains an integer T, the number of test cases. T test cases follows. For each test case, the first line contains contains two numbers N and M, indicating the number of modules and number of signals that Professor Q generates initially. The second line contains M integers, indicating the signals that Professor Q generates initially. Line 3~N + 2, each line describes an module, following the format S, K, E1, E2, … , EK. S represents the signal that start up this module. K represents the total amount of signals that are generated during the lifecircle of this module. And E1 … EK are these signals. For 20% data, all N, M <= 10 For 40% data, all N, M <= 103 For 100% data, all 1 <= T <= 5, N, M <= 105, 0 <= K <= 3, 0 <= S, E <= 105. Hint: HUGE input in this problem. Fast IO such as scanf and BufferedReader are recommended. 输出 For each test case, output a line with N numbers Ans1, Ans2, … , AnsN. Ansi is the number of times that the i-th module is started. In case the answers may be too large, output the answers modulo 142857 (the remainder of division by 142857). 样例输入 3 3 2 123 256 123 2 456 256 456 3 666 111 256 256 1 90 3 1 100 100 2 200 200 200 1 300 200 0 5 1 1 1 2 2 3 2 2 3 4 3 2 4 5 4 2 5 6 5 2 6 7 样例输出 1 1 3 1 2 2 1 1 2 3 5


此题需要厘清模块和信号之间的关系,一个信号可以触发多个模块,一个模块也可以产生多个信号。利用空间换时间,再搜索的时候快速找出关系。使用广度优先搜索解决。 完整代码如下: [cpp] #include<iostream> #include<cstdio> #include<vector> #include<cstring> #include<queue> using namespace std; const int kMaxNMSE = 100005, kMaxK = 5, kMod = 142857; int t, n, m, s, k; int ans[kMaxNMSE]; int signals[kMaxNMSE]; vector<vector<int>> model_signals; vector<vector<int>> signal_models; queue<int> sgls; void Init() { memset(ans, 0, sizeof(ans)); memset(signals, 0, sizeof(signals)); model_signals.clear(); model_signals.resize(kMaxNMSE); signal_models.clear(); signal_models.resize(kMaxNMSE); } void BFS(int sgl) { sgls.push(sgl); while (!sgls.empty()) { int tmp = sgls.front(); sgls.pop(); if (signal_models[tmp].size()) { for (int i = 0; i < signal_models[tmp].size(); i++) { int mdl = signal_models[tmp][i]; ans[mdl]++; for (int j = 0; j < model_signals[mdl].size(); j++) sgls.push(model_signals[mdl][j]); } } } } int main() { //freopen("input.txt", "r", stdin); scanf("%d", &t); while (t–) { Init(); scanf("%d %d", &n, &m); for (int i = 1; i <= m; i++) scanf("%d", &signals[i]); for (int i = 1; i <= n; i++) { scanf("%d %d", &s, &k); signal_models[s].push_back(i); int tmp; while (k–) { scanf("%d", &tmp); model_signals[i].push_back(tmp); } } for (int i = 1; i <= m; i++) BFS(signals[i]); for (int i = 1; i <= n; i++) printf("%d ", ans[i] % kMod); printf("\n"); } return 0; } [/cpp] 本代码提交AC,用时899MS,内存24MB。]]>

hihoCoder 1121-二分图一•二分图判定

hihoCoder 1121-二分图一•二分图判定 #1121 : 二分图一•二分图判定 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 大家好,我是小Hi和小Ho的小伙伴Nettle,从这个星期开始由我来完成我们的Weekly。新年回家,又到了一年一度大龄剩男剩女的相亲时间。Nettle去姑姑家玩的时候看到了一张姑姑写的相亲情况表,上面都是姑姑介绍相亲的剩男剩女们。每行有2个名字,表示这两个人有一场相亲。由于姑姑年龄比较大了记性不是太好,加上相亲的人很多,所以姑姑一时也想不起来其中有些人的性别。因此她拜托我检查一下相亲表里面有没有错误的记录,即是否把两个同性安排了相亲。OK,让我们愉快的暴力搜索吧!才怪咧。对于拿到的相亲情况表,我们不妨将其转化成一个图。将每一个人作为一个点(编号1..N),若两个人之间有一场相亲,则在对应的点之间连接一条无向边。(如下图) 因为相亲总是在男女之间进行的,所以每一条边的两边对应的人总是不同性别。假设表示男性的节点染成白色,女性的节点染色黑色。对于得到的无向图来说,即每一条边的两端一定是一白一黑。如果存在一条边两端同为白色或者黑色,则表示这一条边所表示的记录有误。由于我们并不知道每个人的性别,我们的问题就转化为判定是否存在一个合理的染色方案,使得我们所建立的无向图满足每一条边两端的顶点颜色都不相同。那么,我们不妨将所有的点初始为未染色的状态。随机选择一个点,将其染成白色。再以它为起点,将所有相邻的点染成黑色。再以这些黑色的点为起点,将所有与其相邻未染色的点染成白色。不断重复直到整个图都染色完成。(如下图) 在染色的过程中,我们应该怎样发现错误的记录呢?相信你一定发现了吧。对于一个已经染色的点,如果存在一个与它相邻的已染色点和它的颜色相同,那么就一定存在一条错误的记录。(如上图的4,5节点)到此我们就得到了整个图的算法:选取一个未染色的点u进行染色 遍历u的相邻节点v:若v未染色,则染色成与u不同的颜色,并对v重复第2步;若v已经染色,如果 u和v颜色相同,判定不可行退出遍历。 若所有节点均已染色,则判定可行。 接下来就动手写写吧! 输入 第1行:1个正整数T(1≤T≤10) 接下来T组数据,每组数据按照以下格式给出: 第1行:2个正整数N,M(1≤N≤10,000,1≤M≤40,000) 第2..M+1行:每行两个整数u,v表示u和v之间有一条边 输出 第1..T行:第i行表示第i组数据是否有误。如果是正确的数据输出”Correct”,否则输出”Wrong” 样例输入 2 5 5 1 2 1 3 3 4 5 2 1 5 5 5 1 2 1 3 3 4 5 2 3 5 样例输出 Wrong Correct


本题考察二分图的判定,最简单的染色问题。 提示使用暴力搜索,其实就是BFS广度优先搜索。有一点需要注意的是,无向图可能有多个连通分量,比如下图。 此图有两个连通分量,且两个分量都可以相亲成功,但如果只从第1点开始BFS,则不能遍历到第二个分量,导致失败。所以需要对每一个还未染色的点都开始BFS,如果都尝试过了还有点未染色,二分图判定失败;否则成功。 完整代码如下: [cpp] #include<iostream> #include<cstdio> #include<vector> #include<queue> using namespace std; int n, m; vector<int> colored; vector<vector<int>> path; bool BFS(int start) { colored[start] = 0; int num_colored = 1; queue<int> point; point.push(start); while (!point.empty()) { int current = point.front(); point.pop(); for (int i = 0; i < path[current].size(); i++) { if (colored[path[current][i]] == -1) { if (colored[current] == 0) colored[path[current][i]] = 1; else if (colored[current] == 1) colored[path[current][i]] = 0; num_colored++; point.push(path[current][i]); } else if (colored[current] == colored[path[current][i]]) return false; } } return true; } bool Check() { for (int i = 1; i <= n; i++) if (colored[i] == -1) if (!BFS(i)) return false; return true; } int main() { int t,u,v; scanf("%d", &t); while (t–) { scanf("%d %d", &n, &m); colored.clear(); colored.resize(n + 1, -1); path.clear(); path.resize(n + 1); for (int i = 0; i < m; i++) { scanf("%d %d", &u, &v); path[u].push_back(v); path[v].push_back(u); } if (Check()) printf("Correctn"); else printf("Wrongn"); } return 0; } [/cpp] 本代码提交AC,用时134MS,内存6MB。 ]]>

POJ 2965-The Pilots Brothers' refrigerator

POJ 2965-The Pilots Brothers’ refrigerator The Pilots Brothers’ refrigerator Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 19188 Accepted: 7358 Special Judge Description The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to open a refrigerator. There are 16 handles on the refrigerator door. Every handle can be in one of two states: open or closed. The refrigerator is open only when all handles are open. The handles are represented as a matrix 4х4. You can change the state of a handle in any location [i, j] (1 ≤ i, j ≤ 4). However, this also changes states of all handles in row i and all handles in column j. The task is to determine the minimum number of handle switching necessary to open the refrigerator. Input The input contains four lines. Each of the four lines contains four characters describing the initial state of appropriate handles. A symbol “+” means that the handle is in closed state, whereas the symbol “−” means “open”. At least one of the handles is initially closed. Output The first line of the input contains N – the minimum number of switching. The rest N lines describe switching sequence. Each of the lines contains a row number and a column number of the matrix separated by one or more spaces. If there are several solutions, you may give any one of them. Sample Input -+– —- —- -+– Sample Output 6 1 1 1 3 1 4 4 1 4 3 4 4 Source Northeastern Europe 2004, Western Subregion


这一题和之前做的POJ Problem 1753: Flip Game大同小异,只是这题要求记录搜索路径,并且翻转的时候是同行同列的所有格子都翻转,对之前那题的代码稍作修改即可。 这一题要求翻转的时候是翻转和(i,j)点同行同列的所有点,我们还是通过异或来完成。比如我们要对 [cpp] -+– —- —- -+– [/cpp] 的(0,0)(坐标从0开始)及其行列翻转,应该怎么办呢? 因为1^1=0,1^0=1,所以如果我们要改变某一位,只需要将该位异或1即可。又因为我们输入的时候是从左到右,从上到下的顺序,所以当我们要翻转第0行的时候,只需要异或0xf<<12,0xf<<12相当于把4个二进制1移到最左边(因为第0行在最左边),如果是翻转第i行,则异或0xf<<(4*(3-i));当我们要翻转某一列时,因为同一列的二进制位相差3个位置,所以异或值的形式为二进制0001000100010001,又因为(i,j)这个位置在行翻转的时候已经翻转过了,所以在列翻转时不能再翻转了,否则回到原状态了,所以根据行号的不同,我们可以得到不同的列异或值,分别是curr_xor=0x111,0x1011,0x1101,0x1110,列翻转的公式是next_p.val^=curr_xor<<(3-j);。这个问题用笔在纸上演算一下就会很明白了。 还有一个问题是怎样来记录搜索的路径,我的办法是开辟两个数组: [cpp] typedef struct P//翻转时的格子坐标 { int x,y; }; P trace[MAX_STATUS];//trace[i]到达i状态值时翻转的某个格子坐标 int pre[MAX_STATUS];//pre[i]记录i状态的前一个状态值 [/cpp] pre[i]数组用来记录到达i状态时的前一个状态,而trace[i]则用来记录到达该状态时翻转的格子坐标。这样通过成功状态回溯到开始状态就可以找到所有路径了,具体实现请看下面的代码。 完整代码如下: [cpp] #include<iostream> #include<queue> #include<vector> using namespace std; const int MAX_STATUS=65536;//2^16=65536种,所以[0,65535] const int ALL_1=65535;//全1 int visited[MAX_STATUS]={0};//访问标记 int XOR[4]={0x111,0x1011,0x1101,0x1110};//列异或值 typedef struct POS//position翻转位置 { int val;//当前翻转状态的值 int step;//到当前状态共翻转了多少次 }; typedef struct P//翻转时的格子坐标 { int x,y; }; P trace[MAX_STATUS];//trace[i]到达i状态值时翻转的某个格子坐标 int pre[MAX_STATUS];//pre[i]记录i状态的前一个状态值 //通过反向索引打印结果 void print_result(POS pos,int init_id) { cout<<pos.step<<endl; vector<P> points; points.push_back(trace[pos.val]); int pre_val=pre[pos.val]; while(pre_val!=init_id)//当没有追溯到初始状态时,一直循环 { points.push_back(trace[pre_val]); pre_val=pre[pre_val];//追溯 } int p_num=points.size();//顺着打印出来 for(int i=p_num-1;i>=0;i–) cout<<points[i].x<<" "<<points[i].y<<endl; } //广度优先搜索 void BFS(int init_id) { POS p,next_p; p.val=init_id; p.step=0; visited[p.val]=1; pre[p.val]=-1;//没有前一个状态 queue<POS> Q_P; Q_P.push(p); while(!Q_P.empty()) { p=Q_P.front(); Q_P.pop(); if(p.val==ALL_1) { print_result(p,init_id); return; } for(int i=0;i<4;i++) { int curr_xor=XOR[i];//i不同时,翻转异或的值也不同 for(int j=0;j<4;j++) { next_p.val=p.val; next_p.val^=0xf<<(4*(3-i));//横向翻转 next_p.val^=curr_xor<<(3-j);//纵向翻转 if(visited[next_p.val]==0) { visited[next_p.val]=1; next_p.step=p.step+1; pre[next_p.val]=p.val;//记录前一个状态值 trace[next_p.val].x=i+1;//注意输出的坐标是从1开始到4 trace[next_p.val].y=j+1;//所以这里加1 Q_P.push(next_p); } } } } } int main() { //freopen("input.txt","r",stdin); char c; int id=0; for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { cin>>c; id<<=1;//id=id<<1; if(c==’-‘)//-:1;+:0 id+=1; } } BFS(id); return 0; } [/cpp] 这个版本的代码如果以C++提交,则TLE,如果用G++提交则AC,用时625MS,内存1788K,不知道是什么原因。]]>

POJ 1753-Flip Game

POJ 1753-Flip Game Flip Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 31655 Accepted: 13776 Description Flip game is played on a rectangular 4×4 field with two-sided pieces placed on each of its 16 squares. One side of each piece is white and the other one is black and each piece is lying either it’s black or white side up. Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa. The pieces to be flipped are chosen every round according to the following rules: Choose any one of the 16 pieces. Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any). Consider the following position as an example: bwbw wwww bbwb bwwb Here “b” denotes pieces lying their black side up and “w” denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become: bwbw bwww wwwb wwwb The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal. Input The input consists of 4 lines with 4 characters “w” or “b” each that denote game field position. Output Write to the output file a single integer number – the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it’s impossible to achieve the goal, then write the word “Impossible” (without quotes). Sample Input bwwb bbwb bwwb bwww Sample Output 4 Source Northeastern Europe 2000


题意:翻动格子使得整个棋盘为全黑或者全白,求最少的翻动次数,翻动某一个格子,也会同时翻动其周围的四个格子(如果有的话)。因为某一个格子翻动2次的结果和不翻是一样的;翻动3次的结果和翻动1次的结果是一样的;多个也是一样,所以一个格子最多翻动1次,这样总的翻动次数最多为16次。每个格子有翻和不翻两种情况,所以总共有$$2^{16}$$种情况,可以暴力枚举。 但是一直没有想好用哪种方法,深度搜索还是广度搜索。后来想了想,这是求最优解(最少翻动次数)的题,所以用广度优先搜索的效率应该会高一些,但是在BFS的过程中曾有一个问题困扰了,后面再说。又因为棋盘正好是4*4=16的,可以用int的位运算来表示,b表示1,w表示0,这样棋盘的一个状态就可以用一个int数来表示,翻动格子也可以用位运算来表示,总共的状态数是$$2^{16}$$,取值范围是[0,65535],当棋盘状态是0或者65535时,表示全白或者全黑,这时找到结果。 了解了这些,就可以写出BFS+位运算的代码了,这题我是参考了网上的题解: [cpp] #include<iostream> #include<queue> using namespace std; const int MAX_STATUS=65536;//2^16=65536种,所以[0,65535] const int ALL_0=0;//全0 const int ALL_1=65535;//全1 int ans; int visited[MAX_STATUS]={0};//访问标记 typedef struct POS//position翻转位置 { int val;//当前翻转状态的值 int step;//到当前状态共翻转了多少次 }; //广搜 void BFS(int init_id) { queue<POS> Q_P; POS s,next_p; //初始位置 s.val=init_id; s.step=0; visited[s.val]=1; Q_P.push(s); while(!Q_P.empty()) { s=Q_P.front(); Q_P.pop(); if(s.val==ALL_0||s.val==ALL_1)//找到结果 { ans=s.step; return; } //尝试翻16个位置 for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { next_p.val=s.val; //测试i,翻转[i,j]上下位置的格子 if(i==0) next_p.val^=1<<(11-4*i-j); else if(i==3) next_p.val^=1<<(19-4*i-j); else { next_p.val^=1<<(11-4*i-j); next_p.val^=1<<(19-4*i-j); } //测试j,翻转[i,j]左右位置的格子 if(j==0) next_p.val^=3<<(14-4*i-j); else if(j==3) next_p.val^=3<<(15-4*i-j); else next_p.val^=7<<(14-4*i-j); if(visited[next_p.val]==0)//如果没有访问 { visited[next_p.val]=1;//访问 next_p.step=s.step+1;//翻转次数加1 Q_P.push(next_p);//并加入队列 } } } } ans=-1;//到这里还没有return,说明impossible } int main() { //freopen("input.txt","r",stdin); char c; int id=0; //输入转换 /*bwbw wwww bbwb bwwb 排列成bwbw wwww bbwb bwwb,然后把b换成1,w换成0*/ for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { cin>>c; id<<=1; if(c==’b’)//b=1;w=0 id+=1; } } BFS(id); if(ans==-1) cout<<"Impossible"; else cout<<ans; return 0; } [/cpp] 本代码提交AC,用时79MS,内存508K。 在每一次BFS的时候,我们都要尝试去翻所有16个格子,如果翻动之后达到了一个新的状态(也就是之前没有访问过),说明这个翻动是有效的,把它加入到队列中。在翻动的时候,怎样进行位运算大家可以把i=0,j=0代入到for循环里手算一遍,程序会根据i和j的不同,和不同的数进行异或运算。 之前我说有一个地方让我困惑,就是我之前:第一次BFS的时候肯定把所有格子都翻了一遍,也就是把(0,0)(0,1)…(3,3)翻动的结果都加入到队列中了,那么在第二次BFS的时候,把(0,0)出队,这个时候再来翻所有16个格子,这样不就相当于把所有格子又翻回到初始状态了吗?其实这种想法是不对的,说到底还是对BFS理解不够透彻。**我们在每次BFS的时候都是在队首元素的基础上翻转的,因为我们在while开头执行了s=Q_P.front();Q_P.pop();这么两句**,那么当我们把(0,0)翻转状态出队在去翻转的时候,是在(0,0)翻转状态的基础是再进行翻转的,而这个时候是只有(0,0)这一个格子翻了,其他15个格子都没有翻;同理当把(0,1)出队的时候,也是只有(0,1)翻了,其他15个都没有翻。所以这样BFS下去是能够搜索到所有状态的。 纵观所有BFS题目,套路都是一样的。]]>

POJ 3278-Catch That Cow

POJ 3278-Catch That Cow Catch That Cow Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 48323 Accepted: 15139 Description Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting. Walking: FJ can move from any point X to the points X – 1 or X + 1 in a single minute Teleporting: FJ can move from any point X to the point 2 × X in a single minute. If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it? Input Line 1: Two space-separated integers: N and K Output Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow. Sample Input 5 17 Sample Output 4 Hint The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes. Source USACO 2007 Open Silver


又是一题广度搜索题,经过了上一题的锻炼,基本摸清了做广度优先搜索的套路,而且这一题比上一题还更简单一些。上一题是在三维空间里,而这一题只在一维直线上,只有三种情况:x-1、x+1、2*x。所以可以很快的写出代码: [cpp] #include<iostream> #include<queue> using namespace std; int n,k; int ans; int visited[100002]; const int MAX_DISTANCE=100000;//最大点位置 typedef struct P { int x;//坐标 int curr_min;//从n点到该点走过的时间 }; //广度优先搜索 void bfs() { queue<P> Q_P; P p,next_p; p.x=n; p.curr_min=0; visited[n]=1; Q_P.push(p); while(!Q_P.empty()) { p=Q_P.front(); Q_P.pop(); if(p.x==k) { ans=p.curr_min; return; } if(p.x-1>=0&&visited[p.x-1]==0) { visited[p.x-1]=1; next_p.x=p.x-1; next_p.curr_min=p.curr_min+1; Q_P.push(next_p); } if(p.x+1<=MAX_DISTANCE&&visited[p.x+1]==0) { visited[p.x+1]=1; next_p.x=p.x+1; next_p.curr_min=p.curr_min+1; Q_P.push(next_p); } if(2*p.x<=MAX_DISTANCE&&visited[2*p.x]==0) { visited[2*p.x]=1; next_p.x=2*p.x; next_p.curr_min=p.curr_min+1; Q_P.push(next_p); } } } int main() { cin>>n>>k; bfs(); cout<<ans; return 0; } [/cpp] 本代码提交AC,用时141MS,内存1304K。 仔细琢磨一下做过的深度搜索广度搜索的题,你会发现,一般要求最短距离、最优化问题的,都是用广度优先搜索BFS,比如这一题和上一题;如果只是求某一个可行方案,那么可以用深度搜索,比如POJ Problem 1321: 棋盘问题POJ Problem 1011: Sticks。当然广度搜索题其实也可以用深度搜索题解决,比如用深度搜索找出所有可行解,再求其中的最优解,但是这样的效率明显比只用广度搜索低。]]>

POJ 2251-Dungeon Master

POJ 2251-Dungeon Master Dungeon Master Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 16959 Accepted: 6603 Description You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides. Is an escape possible? If yes, how long will it take? Input The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size). L is the number of levels making up the dungeon. R and C are the number of rows and columns making up the plan of each level. Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a ‘#’ and empty cells are represented by a ‘.’. Your starting position is indicated by ‘S’ and the exit by the letter ‘E’. There’s a single blank line after each level. Input is terminated by three zeroes for L, R and C. Output Each maze generates one line of output. If it is possible to reach the exit, print a line of the form Escaped in x minute(s). where x is replaced by the shortest time it takes to escape. If it is not possible to escape, print the line Trapped! Sample Input 3 4 5 S…. .###. .##.. ###.# ##### ##### ##.## ##… ##### ##### #.### ####E 1 3 3 S## #E# ### 0 0 0 Sample Output Escaped in 11 minute(s). Trapped! Source Ulm Local 1997


经典广度优先搜索题,网上居然把这题分到深度优先搜索了,害的我写好DFS提交后得到个TLE。 首先分析题意,给出一个立体的迷宫,要求从S走到E,求最短距离,其中迷宫中#不能走,.这个能走。我们可以用三维数组char dungeon[MAX_SIZE][MAX_SIZE][MAX_SIZE];来存储迷宫,对于某一个点dungeon[i][j][k],可以试探下面六个点:dungeon[i-1][j][k]、dungeon[i+1][j][k]、dungeon[i][j-1][k]、dungeon[i][j+1][k]、dungeon[i][j][k-1]、dungeon[i][j][k+1],想象你站在空间某一点,你可以往上、下、前、后、左、右走。 如果是深度搜索,试探到某点可行之后,访问它,并以该点为基点继续试探下去,直到到达E点或者无路可走,然后清除访问,每次到达E点,就比较一下这次走过的距离和上次的距离哪个小,当所有点都试探完毕时,就能得到最小值。 如果是广度搜索,以S点为起点,找到所有可行的下一个点,访问他们,并把他们插入到一个队列,然后在队列不为空的情况下循环:取出队首元素,以该元素为基点,找到所有可行的下一个点,访问他们,并把他们加入到队列中。如果广度搜索的过程中遇到E点,则此时走过的距离就是最小值,因为是广度搜索,最先访问到的肯定是最小值,不需要像深度搜索那样再进行比较。 下面是完整代码: [cpp] #include<iostream> #include<queue> using namespace std; const int MAX_SIZE=60; const int MAX_DISTANCE=1024; int L,R,C; char dungeon[MAX_SIZE][MAX_SIZE][MAX_SIZE];//迷宫 //int visited[MAX_SIZE][MAX_SIZE][MAX_SIZE]; int ans; int s_l,s_r,s_c,e_l,e_r,e_c; typedef struct P//点 { int x,y,z;//坐标 int dist;//从S到该点走过的距离 }; //深度搜索,结果正确,但是TLE /*void dfs(int l,int r,int c,int steps) { int visited[MAX_SIZE][MAX_SIZE][MAX_SIZE]={0};//每次初始化 if(l==e_l&&r==e_r&&c==e_c)//搜索成功 { if(steps<ans)//求最小值 ans=steps; return; } //深度搜索的六种情况 if((l-1)>=0&&visited[l-1][r][c]==0&&dungeon[l-1][r][c]==’.’) { visited[l-1][r][c]=1; dfs(l-1,r,c,steps+1);//继续深度搜索 visited[l-1][r][c]=0;//回溯 } if((l+1)<L&&visited[l+1][r][c]==0&&dungeon[l+1][r][c]==’.’) { visited[l+1][r][c]=1; dfs(l+1,r,c,steps+1); visited[l+1][r][c]=0; } if((r-1)>=0&&visited[l][r-1][c]==0&&dungeon[l][r-1][c]==’.’) { visited[l][r-1][c]=1; dfs(l,r-1,c,steps+1); visited[l][r-1][c]=0; } if((r+1)<R&&visited[l][r+1][c]==0&&dungeon[l][r+1][c]==’.’) { visited[l][r+1][c]=1; dfs(l,r+1,c,steps+1); visited[l][r+1][c]=0; } if((c-1)>=0&&visited[l][r][c-1]==0&&dungeon[l][r][c-1]==’.’) { visited[l][r][c-1]=1; dfs(l,r,c-1,steps+1); visited[l][r][c-1]=0; } if((c+1)<C&&visited[l][r][c+1]==0&&dungeon[l][r][c+1]==’.’) { visited[l][r][c+1]=1; dfs(l,r,c+1,steps+1); visited[l][r][c+1]=0; } }*/ //广度搜索 bool bfs(int s_l,int s_r,int s_c) { int visited[MAX_SIZE][MAX_SIZE][MAX_SIZE]={0};//记得每次都要初始化为0,否则会影响下一个case queue<P> P_Q;//队列 P p,next_p; p.x=s_l,p.y=s_r,p.z=s_c,p.dist=0; visited[s_l][s_r][s_c]=1; P_Q.push(p); int l,r,c; while(!P_Q.empty()) { p=P_Q.front(); P_Q.pop(); if(p.x==e_l&&p.y==e_r&&p.z==e_c) { ans=p.dist;//因为是广度搜索,最先到达E,肯定就是最小值 return true; } l=p.x,r=p.y,c=p.z; //广度搜索的六种情况 if((l-1)>=0&&visited[l-1][r][c]==0&&dungeon[l-1][r][c]==’.’) { visited[l-1][r][c]=1; next_p.x=l-1; next_p.y=r; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((l+1)<L&&visited[l+1][r][c]==0&&dungeon[l+1][r][c]==’.’) { visited[l+1][r][c]=1; next_p.x=l+1; next_p.y=r; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((r-1)>=0&&visited[l][r-1][c]==0&&dungeon[l][r-1][c]==’.’) { visited[l][r-1][c]=1; next_p.x=l; next_p.y=r-1; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((r+1)<R&&visited[l][r+1][c]==0&&dungeon[l][r+1][c]==’.’) { visited[l][r+1][c]=1; next_p.x=l; next_p.y=r+1; next_p.z=c; next_p.dist=p.dist+1; P_Q.push(next_p); } if((c-1)>=0&&visited[l][r][c-1]==0&&dungeon[l][r][c-1]==’.’) { visited[l][r][c-1]=1; next_p.x=l; next_p.y=r; next_p.z=c-1; next_p.dist=p.dist+1; P_Q.push(next_p); } if((c+1)<C&&visited[l][r][c+1]==0&&dungeon[l][r][c+1]==’.’) { visited[l][r][c+1]=1; next_p.x=l; next_p.y=r; next_p.z=c+1; next_p.dist=p.dist+1; P_Q.push(next_p); } } return false; } int main() { //freopen("input.txt","r",stdin); while(cin>>L>>R>>C&&L&&R&&C) { ans=MAX_DISTANCE; for(int i=0;i<L;i++) for(int j=0;j<R;j++) for(int k=0;k<C;k++) { cin>>dungeon[i][j][k]; if(dungeon[i][j][k]==’S’) { s_l=i; s_r=j; s_c=k; } else if(dungeon[i][j][k]==’E’) { dungeon[i][j][k]=’.’;//注意这里改成了’.’,便于BFS里搜索判断 e_l=i; e_r=j; e_c=k; } } //dfs(s_l,s_r,s_c,0); if(bfs(s_l,s_r,s_c)) cout<<"Escaped in "<<ans<<" minute(s)."<<endl; else cout<<"Trapped!"<<endl; } return 0; } [/cpp] 本代码提交AC,用时63MS,内存1248K。]]>