面试笔试算法-2

知识点-前缀和

快速求解区间和

原数组:

1234567
1327945

开辟前缀和空间:

01234567
014613222631

求x–y区间和:

sum = sum[y] - sum[x - 1];

LeetCode-13.罗马数字转整数

13. 罗马数字转整数

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

示例 1:

输入: "III"
输出: 3

示例 2:

输入: "IV"
输出: 4

示例 3:

输入: "IX"
输出: 9

示例 4:

输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

思路:

遍历字符串,判断是什么字符,如果是I,X,C,就额外的判断右边的字符,如果是特殊规则中的字符,就进行相减操作.

代码演示:

class Solution {
public:
    int romanToInt(string s) {
        int ans = 0;
        for (int i = 0; i < s.size(); i++) {
            switch(s[i]) {
                case 'V':
                    ans += 5;
                    break;
                case 'L' :
                    ans += 50;
                    break;
                case 'D' :
                    ans += 500;
                    break;
                case 'M':
                    ans += 1000;
                    break;
                case 'I' :
                    if (s[i + 1] == 'V') {
                        ans += 4;
                        i++;
                    } else if (s[i + 1] == 'X') {
                        ans += 9;
                        i++;
                    } else {
                        ans += 1;
                    }
                    break;
                case 'X' :
                    if (s[i + 1] == 'L') {
                        ans += 40;
                        i++;
                    } else if (s[i + 1] == 'C') {
                        ans += 90;
                        i++;
                    } else {
                        ans += 10;
                    }
                    break;
                case 'C':
                    if (s[i + 1] == 'D') {
                        ans += 400;
                        i++;
                    } else if (s[i + 1] == 'M') {
                        ans += 900;
                        i++;
                    } else {
                        ans += 100;
                    }
                    break;
            }
        }
        return ans;
    }
};

LeetCode-14.最长公共前缀

14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入: ["flower","flow","flight"]
输出: "fl"

示例 2:

输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

代码演示:

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if (strs.size() == 0) {
            return "";
        }
        string ans = strs[0];
        for (int i = 1; i < strs.size(); i++)  {
            string t = ans;
            ans = "";
            for (int j = 0; j < strs[i].size() && j < t.size(); j++) {
                if (strs[i][j] == t[j]) {
                    ans += t[j];
                } else {
                    break;
                }
            }
            if (ans == "") {
                break;
            }
        }
        return ans;
    }
};

LeetCode-26

26. 删除排序数组中的重复项

难度简单1723收藏分享切换为英文接收动态反馈

给定一个排序数组,你需要在** 原地** 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

代码演示:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.size() == 0) {
            return 0;
        }
        int p = 1;
        for (int i = 1; i < nums.size(); i++) {
            if (nums[i] != nums[i - 1]) {
                nums[p++] = nums[i];
            }
        }
        return p;
    }
};

LeetCode27

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

代码演示:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if (nums.size() == 0) {
            return 0;
        }
        int p = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != val) {
                nums[p++] = nums[i];
            }
        }
        return p;
    }
};

LeetCode35

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例 1:

输入: [1,3,5,6], 5
输出: 2

示例 2:

输入: [1,3,5,6], 2
输出: 1

示例 3:

输入: [1,3,5,6], 7
输出: 4

示例 4:

输入: [1,3,5,6], 0
输出: 0

思路:

套用0000001111111模型的二分算法

代码演示:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if (nums.size() == 0) {
            return 0;
        }
        if (nums[nums.size() - 1] < target) {
            return nums.size();
        }
        int l = 0, r = nums.size() - 1;
        while (l != r) {
            int mid = (l + r) / 2;
            if (nums[mid] >= target) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        return r;
    }
};

LeetCode38

38. 外观数列

给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:

  • countAndSay(1) = "1"
  • countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。

前五项如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221
第一项是数字 1 
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"

描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。

例如,数字字符串 "3322251" 的描述如下图:

img

示例 1:

输入:n = 1
输出:"1"
解释:这是一个基本样例。

示例 2:

输入:n = 4
输出:"1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = 读 "1" = 一 个 1 = "11"
countAndSay(3) = 读 "11" = 二 个 1 = "21"
countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"

提示:

  • 1 <= n <= 30

代码演示:

class Solution {
public:
    string ans[35] = {"", "1"};
    void func(int s1, int s2) {
        int cnt = 0;
        for (int i = 0; i < ans[s1].size(); i++) {
            if (cnt == 0 || ans[s1][i - 1] == ans[s1][i]) {
                cnt++;
            } else {
                ans[s2] += cnt + '0';
                ans[s2] += ans[s1][i - 1];
                cnt = 1;
            }
        }
        ans[s2] += cnt + '0';
        ans[s2] += ans[s1][ans[s1].size() - 1];
    }
    string countAndSay(int n) {
        for (int i = 2; i <= n; i++) {
            func(i - 1, i);
        }
        return ans[n];
    }
};

LeetCode53

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

思路:

ans[x] = 以x结尾的最大连续和

ans[x] = max(ans[x - 1] + num[x], num[x]);

代码实现:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int ans = INT_MIN, now = 0;
        for (int i = 0; i < nums.size(); i++) {
            now = max(now + nums[i], nums[i]);
            ans = max(ans, now);
        }
        return ans;
    }
};

LeetCode66

66. 加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。

示例 3:

输入:digits = [0]
输出:[1]

提示:

  • 1 <= digits.length <= 100
  • 0 <= digits[i] <= 9

代码演示:

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        digits[digits.size() - 1]++;
        for (int i = digits.size() - 1; i >= 0; i--) {
            if (digits[i] == 10) {
                digits[i] = 0;
                if (i != 0) {
                    digits[i - 1]++;
                } else  {
                    digits.insert(digits.begin(), 1);
                }
            } else {
                break;
            }
        }
        return digits;
    }
};

LeetCode69

69. x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

代码实现:

class Solution {
public:
    int mySqrt(int x) {
        long long l = 0, r = x;
        while (l != r) {
            long long mid = (l + r + 1) / 2;
            if (mid * mid <= x) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        return l;
    }
};

LeetCode70

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

**注意:**给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

代码实现:

class Solution {
public:
    // 方法一
    /*int ans[50] = {0, 1, 2};
    int climbStairs(int n) {
        for (int i = 3; i <= n; i++) {
            ans[i] = ans[i - 1] + ans[i - 2];
        }
        return ans[n];
    }*/
    // 方法二
    int ans[50] = {0};
    int climbStairs(int n) {
       if (n == 1) {
           return 1;
       }
       if (n == 2) {
           return 2;
       }
       if (ans[n]) {
           return ans[n];
       }
       return ans[n] = climbStairs(n - 1) + climbStairs(n - 2);
    }
};

LeetCode88:

88. 合并两个有序数组

给你两个有序整数数组 nums1nums2,请你将 nums2 合并到 nums1 中*,*使 nums1 成为一个有序数组。

说明:

  • 初始化 nums1nums2 的元素数量分别为 mn
  • 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出:[1,2,2,3,5,6]

提示:

  • -10^9 <= nums1[i], nums2[i] <= 10^9
  • nums1.length == m + n
  • nums2.length == n

代码演示:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        for (int i = m + n - 1; i >= 0; i--) {
            if (m == 0 || n != 0 && nums2[n - 1] > nums1[m - 1]) {
                nums1[i] = nums2[n - 1];
                n--;
            } else {
                nums1[i] = nums1[m - 1];
                m--;
            }
        }
    }
};

LeetCode118

118. 杨辉三角

给定一个非负整数 *numRows,*生成杨辉三角的前 numRows 行。

img

在杨辉三角中,每个数是它左上方和右上方的数的和。

示例:

输入: 5
输出:
[
     [1],
    [1,1],
   [1,2,1],
  [1,3,3,1],
 [1,4,6,4,1]
]

代码实现:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> ans;
        if (numRows == 0) {
            return ans;
        }
        ans.push_back({1});
        for (int i = 1; i < numRows; i++) {
            vector<int> v;
            // 每一行前面加1
            v.push_back(1);
            for (int j = 1; j < i; j++) {
                v.push_back(ans[i - 1][j - 1] + ans[i - 1][j]);
            }
            // 每一行后面加1
            v.push_back(1);
            ans.push_back(v);
        }
        return ans;
    }
};

LeetCode121

121. 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路:

  1. 更新当前最小值
  2. 更新结果

代码实现:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() == 0) {
            return 0;
        }
        int ans = 0, mmin = prices[0];
        for (int i = 1; i < prices.size(); i++) {
            ans = max(ans, prices[i] - mmin);
            mmin = min(mmin, prices[i]);
        }
        return ans;
    }
};

LeetCode122

122. 买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 3 * 10 ^ 4
  • 0 <= prices[i] <= 10 ^ 4

代码演示:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() == 0)  {
            return 0;
        }
        int ans = 0;
        for (int i = 1; i < prices.size(); i++) {
            if (prices[i] > prices[i - 1]) {
                ans += (prices[i] - prices[i - 1]);
            }
        }
        return ans;
    }
};

LeetCode136

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

演示代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = 0;
        for (int i = 0; i < nums.size(); i++) {
            ans ^= nums[i];
        }
        return ans;
     }
};

容器的使用:

queue:

类型 + 名, queue<int> que;

  • que.push(1); 入队
  • que.pop(); 出队
  • que.front(); 队首元素
  • que.size(); 元素个数
  • que.empty(); 是否为空

stack:

类型 + 名, stack<int> sta;

  • sta.push(1);
  • sta.pop();
  • sta.top(); 获得栈顶元素
  • sta.size();
  • sta.empty();

注:底层实现是双端队列(dequeue);

队列代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y;
};
int main(int argc, char *grav[])
{
    queue<int> que;
    for (int i = 9; i > 4; i--) {
        que.push(i);
    }
    while (!que.empty()) {
        cout << "que.front() = " << que.front() << "\tque.size() = " << que.size() << endl;
        que.pop();
    }
    queue<node> que1;
    node a;
    a.x = a.y = 6;
    que1.push(a);
    que1.push((node){7, 8});
    cout << que1.front().x << endl;
    que1.pop();
    node temp = que1.front();
    cout << temp.x << " " << temp.y << endl;
    return 0;
}

栈的代码演示:

#include<iostream>
#include <stack>
using namespace std;
int main(int argc, char *grav[])
{
    stack<int> sta;
    for (int i = 9; i > 4; i--) {
        sta.push(i);
    }
    while (!sta.empty()) {
        cout << "sta.top() = " << sta.top() << "\tsta.size() = " <<  sta.size() << endl;
        sta.pop();
    }
    return 0;
}

vector:

类型 + 名 : vector<int> v;

  • v.push_back(5);后面插入
  • v.size(); 容器大小
  • v.insert(1, 6); 插入元素
  • vector<vector<int> > v2; 动态二维数组

priority_queue:

类型 + 名: priority_queue que;

  • que.push(5);
  • que.pop();
  • que.top();
  • que.size();
  • que.empty();

自定义类型如何使用:

需要自己重载<,(>不行):

struct node {
    int x, y;
    bool operator < (const node &b) {
        return this.x > b.x;
    }
};
priority_queue<node> que;

底层实现是堆.

vector代码演示:

#include<iostream>
#include <vector>
using namespace std;
int main(int argc, char *grav[])
{
    vector<int> v;
    for (int i = 105; i <= 110; i++) {
        v.push_back(i);
        cout << v.size() << endl;
    }
    for (int i = 0; i < v.size(); i++) {
        cout << v[i] << endl;
    }
    vector<vector<int> > v1;
    v1.push_back(vector<int>());
    for (int i = 66; i <= 70; i++) {
        v1[0].push_back(i);

    }
    v1.push_back(vector<int>(5, 0));
    vector<int> v2;
    v2.push_back(5);
    v2.push_back(6);
    v2.push_back(7);
    v1.push_back(v2);
    v1.push_back(vector<int>(10, 0));
    for (int i = 0; i < v1.size(); i++) {
        for (int j = 0; j < v1[i].size(); j++) {
            cout << v1[i][j] << "\t";
        }
        cout << endl;
    }
    return 0;
}

priority_queue代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y;
    bool operator < (const node &b) const {
        return this->x > b.x;
    }
};
int main(int argc, char *grav[])
{
    priority_queue<int> que; // 默认从大到小
    que.push(10);
    que.push(20);
    que.push(5);
    que.push(6);
    que.push(1);
    while (!que.empty()) {
        cout << que.top() << endl;
        que.pop();
    }
    // 自定义结构
    priority_queue<node> que1;
    que1.push((node){1, 2});
    que1.push((node){2, 1});
    cout << que1.top().x << endl;
    que1.pop();
    cout << que1.top().x << endl;
    return 0;
}

string:

操作:

  • str.size()/str.length();长度
  • str.find(s2);在str中查找s2,返回下标值,第二个参数是从什么位置开始找,未找到返回值 == string::npos
  • str.insert(x, s2); // 在x位置插入s2
  • str.substr(2); //从下标2的位置开始截取,舍弃前面的字符
  • str.substr(2,2);// 从下标2开始截取2个字符
  • str.replace(x, y, s2); // 从x位置向后替换y个字符,替换成s2

map(键值对):

map<string, int> m;

  • []
  • m[’“123”] = 456;
  • cout << m["123"] << endl;
  • insert
  • find
  • earse
  • count
  • multimap:可以有重复的键
  • unordered_map:hash表实现,无序的

map代码演示:

#include<iostream>
#include <map>
#include <string>
#include <unordered_map>
using namespace std;

struct node {
    int x, y;
    bool operator < (const node &b) const {
        return this->x > b.x;
    }
};

int main(int argc, char *grav[])
{
    // map
    map<string, int> m;
    string a = "123";
    m[a] = 999;
    cout << m["123"] << endl;
    cout << m[a] << endl;
    cout << m["456"] << endl;
    // 自定义结构
    map<node, int> m1;
    node a1;
    m1[a1] = 5;
    // unordered_map
    unordered_map<string, int> unmap;
    string b = "123";
    unmap[b] = 999;
    cout << unmap["123"] << endl;
    cout << unmap[a] << endl;
    cout << unmap["456"] << endl;
    return 0;
}

OJ-383.周末舞会

题目描述

假设在周末舞会上,X 名男士和 Y 名女士进入舞厅时,各自排成一队,并分别按顺序编号。跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。规定每个舞曲只能有一对跳舞者。跳完舞的人回到队尾。输出前 N 首舞曲的跳舞者编号。


输入

一行三个数 X,Y,N。

输出

输出共 N 行,一行两个数,分别代表跳这支舞曲的男士编号和女士编号,两数之间用空格隔开。


样例输入
3 5 6
样例输出
1 1
2 2
3 3
1 4
2 5
3 1

代码演示:

#include<iostream>
#include <queue>
using namespace std;
int main(int argc, char *grav[])
{
    int x, y, n;
    cin >> x >> y >> n;
    queue<int> qx, qy;
    for (int i = 1; i <= x; i++) {
        qx.push(i);
    }
    for (int i = 1; i <= y; i++) {
        qy.push(i);
    }
    for (int i = 1; i <= n; i++) {
        cout << qx.front() << " " << qy.front() << endl;
        qx.push(qx.front());
        qy.push(qy.front());
        qx.pop();
        qy.pop();
    }
    return 0;
}

OJ-378 字符串括号匹配2

题目描述

给出一个字符串,判断其中的左右括号是否匹配。

:需同时判断左右圆括号 ′(′ 和 ′)′ ,左右中括号 ′[′ 和 ′]′ ,左右大括号′{′ 和 ′}′。

不需要考虑括号之间的优先级的问题,也就是说,小括号包含大括号,也是被允许的。


输入

一行一个字符串,以字符@为结尾。

输出

若匹配,输出 YES,若不匹配,则输出 NO。


样例输入
a(cc[])bbb()@
样例输出
YES
样例输入2
a(cc[)]bbb()@
样例输出2
NO

代码演示:

#include<iostream>
#include <stack>
#include <string>
using namespace std;
int main(int argc, char *grav[])
{
    string s;
    cin >> s;
    stack<char> sta;
    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(' || s[i] == '[' || s[i] == '{') {
            sta.push(s[i]);
        } else if (s[i] == ')') {
            if (sta.empty() || sta.top() != '(') {
                cout << "NO" << endl;
                return 0;
            }
            sta.pop();
        } else if (s[i] == ']') {
            if (sta.empty() || sta.top() != '[') {
                cout << "NO" << endl;
                return 0;
            }
            sta.pop();
        } else if (s[i] == '}') {
            if (sta.empty() || sta.top() != '{') {
                cout << "NO" << endl;
                return 0;
            }
            sta.pop();
        }
    }

    if (sta.empty()) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
    return 0;
}

Oj376机器翻译

题目描述

小李的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。

这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。

假设内存中有 M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入 M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。

假设一篇英语文章的长度为 N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。


输入

共 2 行。每行中两个数之间用一个空格隔开。

第一行为两个正整数M,N,代表内存容量和文章的长度。

第二行为 N 个非负整数,按照文章的顺序,每个数(大小不超过 1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

输出

一个整数,为软件需要查词典的次数。


样例输入
3 7
1 2 1 5 4 4 1
样例输出
5
样例说明

整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:

空:内存初始状态为空。

1.1:查找单词1并调入内存。

2. 12:查找单词2并调入内存。

3. 12:在内存中找到单词1。

4. 125:查找单词5并调入内存。

5. 254:查找单词4并调入内存替代单词1。

6. 254:在内存中找到单词4。

7. 541:查找单词1并调入内存替代单词2。

共计查了5次词典。

代码演示:

#include<iostream>
#include <queue>
using namespace std;
int ans, n, m, mark[1005];
int main(int argc, char *grav[])
{
    cin >> m >> n;
    queue<int> que;
    for (int i = 0; i < n; i++) {
        int t;
        cin >> t;
        if (mark[t] == 0) {
            ans++;
            if (que.size() == m) {
                mark[que.front()] = 0;
                que.pop();
            }
            que.push(t);
            mark[t] = 1;
        }
    }
    cout << ans << endl;
    return 0;
}

Oj-379仓库日志

题目描述

某仓库购入新的货物(每次购入的货物均不同)并将一部分老货物发出,这个过程会有软件对数据以日志形式保存,规则如下:

该日志记录了两类操作:第一类操作为入库操作,以及该次入库的货物数量;第二类操作为出库操作。这些记录都严格按时间顺序排列。入库和出库的规则为先进后出,即每次出库操作货物为当前在仓库里所有货物中最晚入库的货物。

为了便于分析,现在加入了第三类查询操作,每次查询时,输出当前仓库数量最多的货物的数量。


输入

包含 N+1 行:

第一行为 1 个正整数 N,对应于日志内所含操作的总数。

接下来的 N 行,分别属于以下三种格式之一:

格式 1: 0 X //一次入库操作,正整数 XX 表示该次入库的货物的数量

格式 2: 1 //一次出库操作,(就当时而言)最后入库的货物出库

格式 3: 2 //一次查询操作,要求分析程序输出当前仓库内数量最多的货物的数量

当仓库为空时你应该忽略出库操作,当仓库为空查询时你应该输出 0。

初始时仓库状态为空。

输出

输出行数等于日志中查询操作的次数。每行为一个正整数,表示查询结果。


样例输入
13
0 1
0 2
2
0 4
0 2
2
1
2
1
1
2
1
2
样例输出
2
4
4
1
0

代码演示:

#include<iostream>
#include <stack>
using namespace std;

int main(int argc, char *grav[])
{
    int n;
    stack<int> g, mmax;
    cin >> n;
    for (int i = 0; i < n; i++) {
        int t;
        cin >> t;
        if (t == 0) {
            cin >> t;
            g.push(t);
            if (mmax.empty()) {
                mmax.push(t);
            } else {
                mmax.push(max(t, mmax.top()));
            }
        } else if (t == 1) {
            if (!g.empty()) {
                g.pop();
                mmax.pop();
            }
        } else if (t == 2) {
            if (g.empty()) {
                cout << 0 << endl;
            } else {
                cout << mmax.top() << endl;
            }
        }
    }
    return 0;
}

Oj-382报数

题目描述

N 个人围成一圈,编号分别为 1,2,3……N ,第一个人从 11 报数,按照编号顺序依次报数,报 M 的人会离开队伍,然后下一个人会继续从 1 开始报数,直到剩下一人,剩下的人的编号是多少?


输入

共一行两个数 N 和 M。

输出

输出一个数表示最后一人的编号。


样例输入
7 3
样例输出
4

思路:

使用队列,当前报的号等于m,就弹出,弹出后当前号置为1,否则放到队尾

代码演示:

#include<iostream>
#include <queue>
using namespace std;

int main(int argc, char *grav[])
{
    int n, m, now = 1;
    cin >> n >> m;
    queue<int> que;
    for (int i = 1; i <= n; i++) {
        que.push(i);
    }
    while (que.size() != 1) {
        if (now == m) {
            que.pop();
            now = 1;
        } else {
            que.push(que.front());
            now++;
            que.pop();
        }
    }
    cout << que.front() << endl;
    return 0;
}

Oj385-海港

题目描述

小李是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。

小李对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第 ii 艘到达的船,他记录了这艘船到达的时间 ti (单位:秒),船上的乘 客数 ki,以及每名乘客的国籍 xi,1,xi,2,…,xi,k。

小李统计了 n 艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的 24 小时( 24 小时= 86400秒)内所有乘船到达的乘客来自多少个不同的国家。

形式化地讲,你需要计算 n 条信息。对于输出的第 i 条信息,你需要统计满足 ti−86400<tp≤ti 的船只 p,在所有的 xp,j 中,总共有多少个不同的数。


输入

第一行输入一个正整数 n,表示小李统计了 n 艘船的信息。

接下来 n 行,每行描述一艘船的信息:前两个整数 ti 和 ki 分别表示这艘船到达海港的时间和船上的乘客数量,接下来 ki 个整数 xi,j 表示船上乘客的国籍。

保证输入的 ti 是递增的,单位是秒;表示从小李第一次上班开始计时,这艘船在第 ti 秒到达海港。

输出

输出 n 行,第 i 行输出一个整数表示第 i 艘船到达后的统计信息。


样例输入1
3
1 4 4 1 2 2
2 2 2 3
10 1 3
样例输出1
3
4
4
样例输入2
4
1 4 1 2 2 3
3 2 2 3
86401 2 3 4
86402 1 5
样例输出2
3
3
3
4
样例说明1

第一艘船在第 1 秒到达海港,最近 24 小时到达的船是第一艘船,共有 4 个乘客, 分别是来自国家 4,1,2,2,共来自 3 个不同的国家;

第二艘船在第 2 秒到达海港,最近 24 小时到达的船是第一艘船和第二艘船,共有 4+2=6 个乘客,分别是来自国家 4,1,2,2,2,3共来自 4 个不同的国家;

第三艘船在第 10 秒到达海港,最近 24 小时到达的船是第一艘船、第二艘船和第 三艘船,共有 4+2+1=7 个乘客,分别是来自国家 4,1,2,2,2,3,3共来自 4 个不同的国家。

样例说明2

第一艘船在第 1 秒到达海港,最近 24小时到达的船是第一艘船,共有 4 个乘客,分别是来自国家 1,2,2,3,共来自 3个不同的国家。

第二艘船在第 3 秒到达海港,最近 24 小时到达的船是第一艘船和第二艘船,共有 4+2=6 个乘客,分别是来自国家 1,2,2,3,2,33,共来自 3 个不同的国家。

第三艘船在第 86401 秒到达海港,最近 24 小时到达的船是第二艘船和第三艘船,共有 2+2=4 个乘客,分别是来自国家 2,3,3,4共来自 3 个不同的国家。

第四艘船在第 86402 秒到达海港,最近 24 小时到达的船是第二艘船、第三艘船和第四艘船,共有 2+2+1=5 个乘客,分别是来自国家 2,3,3,4,5共来自 4 个不同的国家。

思路:

将人和随船到达的时间封装成一个结构体放在队列中,分别记录每次到达船只的人,计算国家数量即可.

代码演示:

#include<cstdio>
#include <queue>
using namespace std;
struct persion {
    int t, c;
};
int n, con, mark[100005];
int main(int argc, char *grav[])
{
    scanf("%d", &n);
    queue<persion> que;
    for (int i = 0; i < n; i++) {
        int at, pcnt;
        scanf("%d%d", &at, &pcnt);
        while (!que.empty() && at - que.front().t >= 86400) {
            mark[que.front().c]--;
            if (mark[que.front().c] == 0) {
                con--;
            }
            que.pop();
        }
        for (int j = 0; j < pcnt; j++) {
            int temp;
            scanf("%d", &temp);
            que.push((persion){at, temp});
            mark[temp]++;
            if (mark[temp] == 1) {
                con++;
            }
        }
        printf("%d\n", con);
    }
    return 0;
}

排列组合与搜索走地图问题

Oj-235.递归实现指数型枚举

题目描述

从 1−n 这 n 个整数中随机选取任意多个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。


输入

输入一个整数 n。(1≤n≤10)

输出

每行一组方案,每组方案中两个数之间用空格分隔。

注意每行最后一个数后没有空格。


样例输入
3
样例输出
1
1 2
1 2 3
1 3
2
2 3
3
样例输入2
4
样例输出2
1
1 2
1 2 3
1 2 3 4
1 2 4
1 3
1 3 4
1 4
2
2 3
2 3 4
2 4
3
3 4
4

思路:

递归的第一层选一个数字

1
2
3
1-n
2-n
3-n
4-n
  1. 这一层从几开始选
  2. 这一层是第几层

代码演示:

#include<iostream>
using namespace std;
int n, num[15];
void p(int deep) {
    for (int i = 1; i <= deep; i++) {
        if (i != 1) {
            cout << " ";
        }
        cout << num[i];
    }
    cout << endl;
}
void func(int s, int deep) {
    for (int i = s; i <= n; i++) {
        num[deep] = i;
        p(deep);
        func(i + 1, deep + 1);
    }
}
int main(int argc, char *grav[])
{
    cin >> n;
    func(1, 1);
    return 0;
}

Oj-236.递归实现组合型枚举

题目描述

从 1−n 这 n 个整数中随机选取 m 个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。


输入

输入两个整数 n,m。(1≤m≤n≤10)

输出

每行一组方案,每组方案中两个数之间用空格分隔。

注意每行最后一个数后没有空格。


样例输入
3 2
样例输出
1 2
1 3
2 3
样例输入2
5 3
样例输出2
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

思路:

  • 从几开始选
  • 还要选几个数
#include<iostream>
using namespace std;

int n, m, num[15], cnt;

void p() {
    for (int i = 0; i < cnt; i++) {
        if (i) {
            cout << " ";
        }
        cout << num[i];
    }
    cout << endl;
}

void func(int s, int left) {
    if (left == 0) {
        p();
        return;
    }
    for (int i = s; i <= n; i++) {
        num[cnt] = i;
        cnt++;
        func(i + 1, left - 1);
        cnt--;
    }
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    func(1, m);
    return 0;
}

Oj-237.递归实现排列型枚举

题目描述

从 1−n 这 n 个整数排成一排并打乱次序,按字典序输出所有可能的选择方案。


输入

输入一个整数 n。(1≤n≤8)

输出

每行一组方案,每组方案中两个数之间用空格分隔。

注意每行最后一个数后没有空格。


样例输入
3
样例输出
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

思路:

和之前的有什么不同?

  • 每一层都是从1–n循环
  • 引入标记数组mark

代码演示:

#include<iostream>
using namespace std;

int n, num[15], mark[15], cnt;

void p() {
    for (int i = 0; i < n; i++) {
        if (i) {
            cout << " ";
        }
        cout << num[i];
    }
    cout << endl;
}
void func(int left) {
    if (left == 0) {
        p();
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (mark[i] == 0) {
            mark[i] = 1;
            num[cnt] = i;
            cnt++;
            func(left - 1);
            cnt--;
            mark[i] = 0;
        }
    }
}
int main(int argc, char *grav[])
{
    cin >> n;
    func(n);
    return 0;
}

深搜的讲解:

1
2
3
4
5
6
7

深搜结果:1–>2–>5–>6–>3–>4

深搜走地图:

S
##
##
T
  1. 方向数组
  2. 存地图

dfs_map:

#include<iostream>
using namespace std;
int n, m, sx, sy;
char mmap[105][105];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};

int func(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (mmap[xx][yy] == 'T') {
            return 1;
        }
        if (mmap[xx][yy] == '.') {
            mmap[xx][yy] = '#';
            if (func(xx, yy) == 1) {
                return 1;
            }
        }
    }
    return 0;
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == 'S') {
                sx = i, sy = j;
            }
        }
    }
    if (func(sx, sy) == 1) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
    return 0;
}

Oj-535.瓷砖:

题目描述

在一个 w∗h 的矩形广场上,每一块 1∗1 的地面都铺设了红色或黑色的瓷砖。小明现在站在某一块黑色的瓷砖上,他可以从此处出发,移动到上下左右四个相邻的且是黑色的瓷砖上。现在,他想知道,通过重复上述移动所能经过的黑色瓷砖数。


输入

第一行两个正整数 h,w。(2≤h,w≤50)

接下来输入一个二维字符矩阵,每个字符为 “.”,"#","@",分别代表黑色瓷砖,红色瓷砖,初始位置。

输出

输出一个整数,表示可以到达的瓷砖数。


样例输入
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
样例输出
59

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 2≤h,w≤50

代码演示:

#include<iostream>
using namespace std;

int n, m, ans = 1, sx, sy;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char mmap[105][150];

void func(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (mmap[xx][yy] == '.') {
            ans++;
            mmap[xx][yy] = 0;
            func(xx, yy);
        }
    }
}

int main(int argc, char *grav[])
{
    cin >> m >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == '@') {
                sx = i, sy = j;
            }
        }
    }
    func(sx, sy);
    cout << ans << endl;
    return 0;
}

Oj-397.僵尸来袭

题目描述

9102年,小明和李华正在怀旧的玩植物大战僵尸重置版,重置版多了一个道具—卫星地图,使用此道具后就能知道后院外(n 行 m 列)的每个格子有多少个僵尸, 0 代表这个格子上没有僵尸,其余数代表僵尸的个数。若某一个格子上有僵尸,且在这个格子上下左右的某个格子上也有僵尸,那么他们为同一波僵尸,现求后院外还有多少波僵尸。


输入

第一行一个整数 n,m (5≤n,m≤100)。

接下来 n 行 m 列,由数字组成的 n×m 的方阵。

输出

输出剩余僵尸有几波。


样例输入
5 6
0 1 2 3 4 0
1 0 0 0 0 0
2 9 3 0 2 4
0 0 2 0 2 8
1 0 0 0 0 0
样例输出
4

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 5≤n,m≤100

代码演示:

#include<iostream>
using namespace std;

int n, m, ans, mmap[105][105];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};

void func(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if(mmap[xx][yy] != 0) {
            mmap[xx][yy] = 0;
            func(xx, yy);
        }
    }
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (mmap[i][j] != 0) {
                ans++;
                mmap[i][j] = 0;
                func(i, j);
            }
        }
    }
    cout << ans << endl;
    return 0;
}

Oj-536.最大黑色区域

题目描述

现在给出一个 n∗m 的二维矩阵,矩阵上的每个点只可能是 0 (代表白色)或 1 (代表黑色)。现规定某一点的颜色与它的上下左右某点的颜色相同,则它们为同一区域,现求最大黑色区域的大小。


输入

第一行两个正整数 n,m。(1≤n,m≤100)

接下来输入一个二维字符矩阵,每个字符为 0 或 1。

输出

输出一个整数,表示可以最大黑色区域面积。


样例输入
5 6
011001
110101
010010
000111
101110
样例输出
7

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤n,m≤100

代码演示:

#include<iostream>
using namespace std;
int n, m, ans, temp;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char mmap[105][105];
void func(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (mmap[xx][yy] == '1') {
            temp++;
            mmap[xx][yy] = 0;
            func(xx, yy);
        }
    }
}
int main(int argc, char *grav[])
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (mmap[i][j] == '1') {
                temp = 1;
                mmap[i][j] = 0;
                func(i, j);
                ans = max(ans, temp);
            }
        }
    }
    cout << ans << endl;
    return 0;
}

Oj-396.填涂颜色

题目描述

由数字 0 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 1 构成,围圈时只走上下左右 4 个方向。现要求把闭合圈内的所有空间都填写成 2。例如:6×6 的方阵 (n=6),涂色前和涂色后的方阵如下:

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

输入

第一行一个整数 n (5≤n≤30)。

接下来 n 行,由 0 和 1 组成的 n×n 的方阵。

方阵内只有一个闭合圈,圈内至少有一个 0。

输出

已经填好数字 2 的完整方阵。


样例输入
6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
样例输出
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 5≤n≤30

思路:

  1. 将原有的地图外围填涂上一圈零
  2. 以外围的零为起点搜索所有连通的零并将其改为3.
  3. 此时剩下的0全都是被1包围着的,输出时将等于3的值改为0,等于0的改为2即可

代码演示:

#include<iostream>
using namespace std;

int n, mmap[50][50];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};

void func(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (xx < 0 || yy > 0 || xx > n + 1 || yy > n + 1) {
            continue;
        }
        if (mmap[xx][yy] == 0) {
            mmap[xx][yy] = 3;
            func(xx, yy);
        }
    }
}

int main(int argc, char *grav[])
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> mmap[i][j];
        }
    }
    mmap[0][0] = 3;
    func(0, 0);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (j != 1) {
                cout << " ";
            }
            if (mmap[i][j] == 3) {
                cout << 0;
            } else if (mmap[i][j] == 0){
                cout << 2;
            } else {
                cout << 1;
            }
        }
        cout << endl;
    }
    return 0;
}

Oj-404.01迷宫简易版

题目描述

有一个仅由数字 0 与 1 组成的 n×m 格迷宫。若你位于一格 0 上,那么你可以移动到相邻 4 格中的某一格 1 上,同样若你位于一格 1 上,那么你可以移动到相邻 4 格中的某一格 0 上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。


输入

第 1 行为两个正整数 n,m。

下面 n 行,每行 m 个字符,字符只可能是 0 或者 1,字符之间没有空格。

接下来一行给出一个点的在坐标。

输出

一个整数表示该点能移动到的格子数(包括自身)。


样例输入
2 3
011
100
2 3
样例输出
2

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤n,m≤3000

思路:

  1. 定义一个标记数组mark,用来去重
  2. 判断边界条件

代码演示:

#include<iostream>
using namespace std;

int n, m, mark[3005][3005], ans, sx, sy;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char mmap[3005][3005];

void func(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (xx < 1 || yy < 1 || xx > n || yy > m || mark[xx][yy] == 1) {
            continue;
        }
        if (mmap[x][y] != mmap[xx][yy]) {
            ans++;
            mark[xx][yy] = 1;
            func(xx, yy);
        }
    }
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    cin >> sx >> sy;
    ans = 1;
    mark[sx][sy] = 1;
    func(sx, sy);
    cout << ans << endl;
    return 0;
}

Oj-405.01迷宫

题目描述

有一个仅由数字 0 与 1 组成的 n×m 格迷宫。若你位于一格 0 上,那么你可以移动到相邻 4 格中的某一格 1 上,同样若你位于一格 1 上,那么你可以移动到相邻 4 格中的某一格 0 上。

你的任务是:对于给定的迷宫,询问 k 次从某一格开始能移动到多少个格子(包含自身)。


输入

第 11 行为两个正整数 n,m,k。

下面 n 行,每行 m 个字符,字符只可能是 0 或者 1,字符之间没有空格。

接下来 k 行每行给出一个点的在坐标。

输出

对于每个坐标输出一行一个整数表示该点能移动到的格子数(包括自身)。


样例输入
2 3 4
011
100
1 1
2 2
1 3
2 3
样例输出
4
4
2
2

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤n,m≤3000,1≤k≤30000

思想:

  1. 总体思想:求出每个连通区域能走的步数,然后每次询问时访问结果数组即可.
  2. 需要使用队列来存储每个连通区域中每个点能走的步数

代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y;
};
queue<node> que;
int n, m, ans[3005][3005], temp;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char mmap[3005][3005];
int k;
void check() {
    while (!que.empty()) {
        node t = que.front();
        que.pop();
        ans[t.x][t.y] = temp;
    }
}
void func(int x, int y) {
    que.push((node){x, y});
    for (int i = 0; i < 4; i++) {
        int xx = x + dir[i][0];
        int yy = y + dir[i][1];
        if (xx < 1 || yy < 1 || xx > n || yy > m || ans[xx][yy] != 0) {
            continue;
        }
        if (mmap[x][y] != mmap[xx][yy]) {
            ans[xx][yy] = 1;
            temp++;
            func(xx, yy);
        }
    }
}
int main(int argc, char *grav[])
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (ans[i][j] == 0) {
                temp = 1;
                ans[i][j] = 1;
                func(i, j);
                check();
            }
        }
    }
    for (int i = 0; i < k; i++) {
        int a, b;
        cin >> a >> b;
        cout << ans[a][b] << endl;
    }
    return 0;
}

广搜走地图:

搜索队列

Oj-399.小明吃饭

题目描述

难得的休息日,小明起床时就已经 11 点多了,他常去的馅饼店一般 12 点就没饭了,他需要赶紧洗漱,出门,然后去馅饼店吃饭,到了小区大门口他傻眼了,前几天贴的修路公告成为了现实,现在小区门外已经成为工地,到处都是路障。小区门口到馅饼店的路形如一个 n×m 的二维矩阵,能走的道路被标记成了 .,路障被标记成了 #,小区大门被标记成了 2,馅饼店被标记成了 3,小明可以在矩阵上上下左右移动,每移动一次代表走了一步,现求小明从小区门口最少走多少步才能到达馅饼店,若无法到达则输出 −1。


输入

第一行两个整数 n,m。(5≤n,m≤500)

接下来 n 行,每行 m 个字符表示道路的状态。

输出

一个整数表示从小区门口到馅饼店的最少步数,若无法到达则输出 −1。


样例输入
5 6
2.....
###.#.
....#.
..###.
....3.
样例输出
10

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 5≤n,m≤500

代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};

int n, m, sx, sy;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char mmap[505][505];
int main(int argc, char *grav[])
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == '2') {
                sx = i, sy = j;
            }
        }   
    }
    queue<node> que;
    que.push((node){sx, sy, 0});
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 4; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (mmap[x][y] == '3') {
                cout << temp.step + 1 << endl;
                return 0;
            }
            if (mmap[x][y] == '.') {
                mmap[x][y] = 0;
                que.push((node){x, y, temp.step + 1});
            }
        }
    }
    cout << -1 << endl;
    return 0;
}

Oj-304.骑士风度的牛

题目描述

农民约翰有很多牛,他想交易其中一头被Don称为骑士的牛。这头牛有一个独一无二的超能力,在农场里像骑士一样地跳(就是我们熟悉的象棋中马的走法)。虽然这头神奇的牛不能跳到树上和石头上,但是它可以在牧场上随意跳,我们把牧场用一个 x,y 的坐标图来表示。

这头神奇的牛像其它牛一样喜欢吃草,给你一张地图,上面标注了骑士的开始位置,树、灌木、石头以及其它障碍的位置,除此之外还有一捆草。现在你的任务是,确定骑士要想吃到草,至少需要跳多少次。骑士的位置用 KK 来标记,障碍的位置用 ∗ 来标记,草的位置用 H 来标记。


输入

第一行输入两个数,表示农场的列数 (≤150) 和行数 (≤150)。

接下来输入地图。

输出

一个数,表示跳跃的最小次数。


样例输入
10 11
..........
....*.....
..........
...*.*....
.......*..
..*..*...H
*.........
...*...*..
.K........
...*.....*
..*....*..
样例输出
5

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 地图大小 ≤150

代码实现:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};

int n, m;
int dir[8][2] = {1, 2, 1, -2, -1, 2, -1, -2, 2, 1, 2, -1, -2, 1, -2, -1};
char mmap[200][200];

int main(int argc, char *grav[])
{
    cin >> m >> n;
    queue<node> que;
    for (int i = 5; i < n + 5; i++) {
        for (int j = 5; j < m + 5; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == 'K') {
                que.push((node){i, j, 0});
            }
        }
    }
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 8; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (mmap[x][y] == 'H') {
                cout << temp.step + 1 << endl;
                return 0;
            }
            if (mmap[x][y] == '.') {
                que.push((node){x, y, temp.step + 1});
                mmap[x][y] = '#';
            }
        }
    }
    cout << -1 << endl;
    return 0;
}

Oj-398.马的遍历

题目描述

有一个 n 行 m 列的棋盘( 1<n,m≤400 ),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。


输入

一行四个整数,分别表示棋盘大小 n,m 和马的位置 x,y。

输出

一个 n∗m 的矩阵,代表马到达某个点最少要走几步,每两个数之间用空格隔开,若此点不可达则输出 −1。


样例输入
3 3 1 1
样例输出
0 3 2    
3 -1 1    
2 1 4    

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1<n,m≤400

#include<iostream>
#include <cstring>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};
int num[405][405], n, m, sx, sy;
int dir[8][2] = {1, 2, 1, -2, -1, 2, -1, -2, 2, 1, 2, -1, -2, 1, -2, -1};
int main(int argc, char *grav[])
{
    memset(num, -1, sizeof(num));
    cin >> n >> m >> sx >> sy;
    num[sx][sy] = 0;
    queue<node> que;
    que.push((node){sx, sy, 0});
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 8; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (x < 1 || y < 1 || x > n || y > m || num[x][y] != -1) {
                continue;
            }
            num[x][y] = temp.step + 1;
            que.push((node){x, y, num[x][y]});
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (j != 1) {
                cout << " ";
            }
            cout << num[i][j];
        }
        cout << endl;
    }
    return 0;
}

Oj-401.奇怪的象棋游戏升级版

题目描述

小明和李华在玩一种奇怪的象棋游戏,这个棋盘的大小为 500×500,有一种棋子,既能“马走日”也能“象走田”,现在棋盘上只有这么一个棋子在位置 X,Y 处,现求这个棋子走到 (1,1)点最少需要走多少步。


输入

第一行输入一个整数 n 表示询问次数。(1≤n≤2000)

接下来 n 行,每行两个数表示这次询问的棋子坐标 X,Y。(1≤x,y≤500)

输出

对于每次询问输出一行一个数表示最少需要的步数。


样例输入
2
12 16
18 10
样例输出
8
9

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤n≤2000

代码实现:

#include<iostream>
#include <queue>
#include <cstring>
using namespace std;
struct node {
    int x, y, step;
};
int n, num[505][505];
int dir[12][2] = {
    1, 2, 1, -2, -1, 2, -1, -2,
    2, 1, 2, -1, -2, 1, -2, -1,
    2, 2, 2, -2, -2, 2, -2, -2
};

int main(int argc, char *grav[])
{
    memset(num, -1, sizeof(num));
    queue<node> que;
    que.push((node){1, 1, 0});
    num[1][1] = 0;
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 12; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (x < 1 || y < 1 || x > 500 || y > 500 || num[x][y] != -1) {
                continue;
            }
            num[x][y] = temp.step + 1;
            que.push((node){x, y, temp.step + 1});
        }
    }
    cin >> n;
    for (int i = 0; i < n; i++) {
        int a, b;
        cin >> a >> b;
        cout << num[a][b] << endl;
    }
    return 0;
}

Oj-303.矩阵距离一

题目描述

假设我们有一个矩阵,其元素值非 0 即 1:

a11 … … a1m

… … … … …

an1 … … anm

定义 aij 与 akl 之间的距离为 D(aij,akl)=abs(i−k)+abs(j−l)。

现求每个元素到最近的元素值为 1 的元素的距离。


输入

输入文件的第一行为两个整数,分别代表 n 和 m。

接下来的 n 行,第 i 行的第 j 个字符代表 aij。

输出

输出包含 N 行,每行 M 个用空格分开的数字,其中第 ii 行第 j 个数字代表 Min(D(aij,axy)1≤x≤N,1≤y≤m,且 axy=1。


样例输入
3 4
0001
0011
0110
样例输出
3 2 1 0
2 1 0 0
1 0 0 1

数据规模与约定

时间限制:10 s

内存限制:256 M

100% 的数据保证 1≤M,N≤1000

代码演示:

#include<iostream>
#include <queue>
#include <cstring>
using namespace std;
struct node {
    int x, y, step;
};
int n, m, num[1005][1005];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
char mmap[1005][1005];
int main(int argc, char *grav[])
{
    memset(num, -1, sizeof(num));
    cin >> n >> m;
    queue<node> que;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mmap[i][j];
            if (mmap[i][j] == '1') {
                que.push((node){i, j, 0});
                num[i][j] = 0;
            }
        }
    }
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 4; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (mmap[x][y] != 0 && num[x][y] == -1) {
                num[x][y] = temp.step + 1;
                que.push((node){x, y, num[x][y]});
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (j != 1) {
                cout << " ";
            }
            cout << num[i][j];
        }
        cout << endl;
    }
    return 0;
}

Oj-305.乳草的入侵

题目描述

约翰一直努力让他的草地充满鲜美多汁的而又健康的牧草。可惜天不从人愿,他在植物大战人类中败下阵来。邪恶的乳草已经在他的农场的西北部份佔领了一片立足之地。

草地像往常一样,被分割成一个高度为 Y(1≤y≤100),宽度为 X(1≤x≤100) 的直角网格。(1,1) 是左下角的格(也就是说坐标排布跟一般的 X,Y 坐标相同)。乳草一开始占领了格 (Mx,My)。每个星期,乳草传播到已被乳草占领的格子四面八方的每一个没有很多石头的格(包括垂直与水平相邻的和对角线上相邻的格)。1 周之后,这些新占领的格又可以把乳草传播到更多的格里面了。

骑士牛想要在草地被乳草完全占领之前尽可能的享用所有的牧草。她很好奇到底乳草要多久才能占领整个草地。如果乳草在 0 时刻处于格 (Mx,My),那么还在那个时刻它们可以完全占领入侵整片草地呢。草地由一个矩阵表示。. 表示草,而 ∗ 表示大石。比如这个 X=4,Y=3的例子。

 ....
 ..*.
 .**.

如果乳草一开始在左下角(第 1 排,第 1 列),那么草地的地图将会以如下态势发展:

  ....  ....  MMM.  MMMM  MMMM  
  ..*.  MM*.  MM*.  MM*M  MM*M  
  M**.  M**.  M**.  M**.  M**M  
   0     1     2     3     4    星期数

乳草会在4星期后占领整片土地。


输入

第一行输入四个整数 X,Y,Mx,My。

接下来输入地图。

输出

输出一个整数,表示乳草占领整个土地的周数。


样例输入
4 3 1 1
....
..*.
.**.
样例输出
4

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 地图大小 ≤100

代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};
int n, m, sx, sy;
int dir[8][2] = {0, 1, 1, 0, 0, -1, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1};
char mmap[105][105];

int main(int argc, char *grav[])
{
    cin >> m >> n >> sy >> sx;
    sx = n - sx + 1;
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    queue<node> que;
    que.push((node){sx, sy, 0});
    mmap[sx][sy] = 0;
    int ans = 0;
    while (!que.empty()) {
        node temp = que.front();
        ans = temp.step;
        que.pop();
        for (int i = 0; i < 8; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1]; 
            if (mmap[x][y] == '.') {
                mmap[x][y] = 0;
                que.push((node){x, y, temp.step + 1});
            }
        }
    }
    cout << ans << endl;
    return 0;
}

Oj-529.龙与虫

题目描述

给出一张 n∗m 的地图,在地图上有一只虫,样子却很像龙,而且嘴能快速的直射喷出一种毒液,瞬间杀死敌人。

现在假设虫的初始位置在 x1,y1,另外在 x2,y2 处有一个敌人。假设虫移动一步需要单位 1 的时间,而杀死敌人不需要时间,并且虫的毒液射程无穷大,但毒液不能穿透障碍物,虫可以向四个方向移动,向八个方向攻击,求虫最少需要多少时间才能消灭敌人。


输入

第一行两个整数 n,m。(1≤n,m≤128)

接下来是一个n∗m 的矩阵,O 表示空地,X 表示障碍物。

再接下来每行对应一组数据,对于每组数据,一行四个整数分别表示 x2,y2,x1,y1,数据不多于十组。

读入 0,0,0,0 表示程序结束。

输出

对于每组数据,输出一行一个数,表示消灭敌人的最短时间,若无法消灭敌人,则输出 Impossible!。


样例输入
3 4
OXXO
XXOO
XOOO
3 2 2 4
3 3 1 1
0 0 0 0
样例输出
1
Impossible!

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤n,m≤128

代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};

int n, m;
int dir[8][2] = {0, 1, 1, 0, 0, -1, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1};
char mmap[150][150];

int func() {
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    if (!a) {
        return 0;
    }
    int mark[150][150] = {0};
    // 标记可以射的点
    for (int i = 0; i < 8; i++) {
        for (int j = 1; 1; j++) {
            int x = a + dir[i][0] * j;
            int y = b + dir[i][1] * j;
            if (mmap[x][y] != 'O') {
                break;
            }
            mark[x][y] = 1;
        }
    }
    if (mark[c][d] == 1) {
        cout << 0 << endl;
        return 1;
    }
    queue<node> que;
    que.push((node){c, d, 0});
    mark[c][d] = 2;
    // 搜索
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 4; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (mark[x][y] == 1) {
                cout << temp.step + 1 << endl;
                return 1;
            }
            if (mmap[x][y] == '0' && mark[x][y] != 2) {
                que.push((node){x, y, temp.step + 1});
                mark[x][y] = 2;
            }
        }
    }
    cout << "Impossible!" << endl;
    return 1;
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    // 输入地图
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    while (func()) {

    }
    return 0;
}

Oj527.飞跃原野

题目描述

在一片广阔的土地上,有一个鸟人,他需要从这里穿过原野,回到基地。这片原野上,有平地 §、有湖泊 (L),因为鸟人可以飞,所以呢,有的时候,他可以飞越湖泊。现在,鸟人需要用最快的时间,回到基地。

假设原野是一个 m∗n 的矩阵,有两种地形,用 P 和 L 表示。鸟人只能停留在平地上。他目前处在 (1,1) 这个位置,而目的地是 (m,n),起点和目的地必为平地。他可以向上下左右四个方向移动,或者飞行。每移动一格需要 1 个单位时间。而飞行无论飞多远,都只需要 1 个单位时间。飞行的途中不可以变方向,也就是说飞行也只能是上下左右四个方向。并且一次飞行最终必须降落在平地上。当然,受到能量的限制,鸟人不能无限制的飞行,他总共最多可以飞行的距离为 D(总计飞行距离,不是一次飞行距离)。现求飞到目的地的最短时间,若无法到达则输出 impossible。


输入

第一行三个整数 m,n,D。(1≤m,n,D≤100)

接下来是一个 m∗n 的矩阵,表示原野。

输出

输出一个数,表示最短时间,如果无法到达,则输出 impossible。


样例输入
4 4 2
PLLP
PPLP
PPPP
PLLP
样例输出
5

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤m,n,D≤100

总结:

欧拉计划:

滑动窗口法

方向数组怎么用

记忆数组的使用

什么叫记忆化?

大整数

最简单的动态规划

二分专题:

sort怎么用

朴素二分

二分查找特殊情况:

二分答案

做题周:

leetcode题 2sum

Oj题:模拟/贪心/枚举

STL:

容器:queue、stack、priority_queue、string、vector、map

map–>m -->unmap

六大组件:

搜索:

递归、排列组合、走地图、二进制按位存储状态

样例输入
4 3 1 1
....
..*.
.**.
样例输出
4

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 地图大小 ≤100

代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};
int n, m, sx, sy;
int dir[8][2] = {0, 1, 1, 0, 0, -1, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1};
char mmap[105][105];

int main(int argc, char *grav[])
{
    cin >> m >> n >> sy >> sx;
    sx = n - sx + 1;
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    queue<node> que;
    que.push((node){sx, sy, 0});
    mmap[sx][sy] = 0;
    int ans = 0;
    while (!que.empty()) {
        node temp = que.front();
        ans = temp.step;
        que.pop();
        for (int i = 0; i < 8; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1]; 
            if (mmap[x][y] == '.') {
                mmap[x][y] = 0;
                que.push((node){x, y, temp.step + 1});
            }
        }
    }
    cout << ans << endl;
    return 0;
}

Oj-529.龙与虫

题目描述

给出一张 n∗m 的地图,在地图上有一只虫,样子却很像龙,而且嘴能快速的直射喷出一种毒液,瞬间杀死敌人。

现在假设虫的初始位置在 x1,y1,另外在 x2,y2 处有一个敌人。假设虫移动一步需要单位 1 的时间,而杀死敌人不需要时间,并且虫的毒液射程无穷大,但毒液不能穿透障碍物,虫可以向四个方向移动,向八个方向攻击,求虫最少需要多少时间才能消灭敌人。


输入

第一行两个整数 n,m。(1≤n,m≤128)

接下来是一个n∗m 的矩阵,O 表示空地,X 表示障碍物。

再接下来每行对应一组数据,对于每组数据,一行四个整数分别表示 x2,y2,x1,y1,数据不多于十组。

读入 0,0,0,0 表示程序结束。

输出

对于每组数据,输出一行一个数,表示消灭敌人的最短时间,若无法消灭敌人,则输出 Impossible!。


样例输入
3 4
OXXO
XXOO
XOOO
3 2 2 4
3 3 1 1
0 0 0 0
样例输出
1
Impossible!

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤n,m≤128

代码演示:

#include<iostream>
#include <queue>
using namespace std;
struct node {
    int x, y, step;
};

int n, m;
int dir[8][2] = {0, 1, 1, 0, 0, -1, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1};
char mmap[150][150];

int func() {
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    if (!a) {
        return 0;
    }
    int mark[150][150] = {0};
    // 标记可以射的点
    for (int i = 0; i < 8; i++) {
        for (int j = 1; 1; j++) {
            int x = a + dir[i][0] * j;
            int y = b + dir[i][1] * j;
            if (mmap[x][y] != 'O') {
                break;
            }
            mark[x][y] = 1;
        }
    }
    if (mark[c][d] == 1) {
        cout << 0 << endl;
        return 1;
    }
    queue<node> que;
    que.push((node){c, d, 0});
    mark[c][d] = 2;
    // 搜索
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        for (int i = 0; i < 4; i++) {
            int x = temp.x + dir[i][0];
            int y = temp.y + dir[i][1];
            if (mark[x][y] == 1) {
                cout << temp.step + 1 << endl;
                return 1;
            }
            if (mmap[x][y] == '0' && mark[x][y] != 2) {
                que.push((node){x, y, temp.step + 1});
                mark[x][y] = 2;
            }
        }
    }
    cout << "Impossible!" << endl;
    return 1;
}

int main(int argc, char *grav[])
{
    cin >> n >> m;
    // 输入地图
    for (int i = 1; i <= n; i++) {
        cin >> &mmap[i][1];
    }
    while (func()) {

    }
    return 0;
}

Oj527.飞跃原野

题目描述

在一片广阔的土地上,有一个鸟人,他需要从这里穿过原野,回到基地。这片原野上,有平地 §、有湖泊 (L),因为鸟人可以飞,所以呢,有的时候,他可以飞越湖泊。现在,鸟人需要用最快的时间,回到基地。

假设原野是一个 m∗n 的矩阵,有两种地形,用 P 和 L 表示。鸟人只能停留在平地上。他目前处在 (1,1) 这个位置,而目的地是 (m,n),起点和目的地必为平地。他可以向上下左右四个方向移动,或者飞行。每移动一格需要 1 个单位时间。而飞行无论飞多远,都只需要 1 个单位时间。飞行的途中不可以变方向,也就是说飞行也只能是上下左右四个方向。并且一次飞行最终必须降落在平地上。当然,受到能量的限制,鸟人不能无限制的飞行,他总共最多可以飞行的距离为 D(总计飞行距离,不是一次飞行距离)。现求飞到目的地的最短时间,若无法到达则输出 impossible。


输入

第一行三个整数 m,n,D。(1≤m,n,D≤100)

接下来是一个 m∗n 的矩阵,表示原野。

输出

输出一个数,表示最短时间,如果无法到达,则输出 impossible。


样例输入
4 4 2
PLLP
PPLP
PPPP
PLLP
样例输出
5

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 1≤m,n,D≤100

总结:

欧拉计划:

滑动窗口法

方向数组怎么用

记忆数组的使用

什么叫记忆化?

大整数

最简单的动态规划

二分专题:

sort怎么用

朴素二分

二分查找特殊情况:

二分答案

做题周:

leetcode题 2sum

Oj题:模拟/贪心/枚举

STL:

容器:queue、stack、priority_queue、string、vector、map

map–>m -->unmap

六大组件:

搜索:

递归、排列组合、走地图、二进制按位存储状态

存、起点、终端、转移、去重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值