• LeetCode 1700. 无法吃午餐的学生数量:真假模拟(极简代码) + 奇技淫巧


    【LetMeFly】1700.无法吃午餐的学生数量:真假模拟(极简代码) + 奇技淫巧

    力扣题目链接:https://leetcode.cn/problems/number-of-students-unable-to-eat-lunch/

    学校的自助午餐提供圆形和方形的三明治,分别用数字 01 表示。所有学生站在一个队列里,每个学生要么喜欢圆形的要么喜欢方形的。
    餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个 里,每一轮:

    • 如果队列最前面的学生 喜欢 栈顶的三明治,那么会 拿走它 并离开队列。
    • 否则,这名学生会 放弃这个三明治 并回到队列的尾部。

    这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。

    给你两个整数数组 studentssandwiches ,其中 sandwiches[i] 是栈里面第 i​​​​​​ 个三明治的类型(i = 0 是栈的顶部), students[j] 是初始队列里第 j​​​​​​ 名学生对三明治的喜好(j = 0 是队列的最开始位置)。请你返回无法吃午餐的学生数量。

    示例 1:

    输入:students = [1,1,0,0], sandwiches = [0,1,0,1]
    输出:0 
    解释:
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,0,0,1]。
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,0,1,1]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [0,1,1],三明治栈为 sandwiches = [1,0,1]。
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,1,0]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1,0],三明治栈为 sandwiches = [0,1]。
    - 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,1]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1],三明治栈为 sandwiches = [1]。
    - 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [],三明治栈为 sandwiches = []。
    所以所有学生都有三明治吃。
    

    示例 2:

    输入:students = [1,1,1,0,0,1], sandwiches = [1,0,0,0,1,1]
    输出:3
    

    提示:

    • 1 <= students.length, sandwiches.length <= 100
    • students.length == sandwiches.length
    • sandwiches[i] 要么是 0 ,要么是 1
    • students[i] 要么是 0 ,要么是 1

    方法一:真模拟

    真模拟就是真的按照题意,将students变成队列,sandwich变成栈

    然后每次从头到尾依次出队,遇到与栈顶元素相同的就“走人”

    所有同学都出队过一次也没有匹配到三明治的话,谁都吃不到了,就返回剩余学生的数量。

    • 时间复杂度 O ( n 2 ) O(n^2) O(n2),其中 n n n是学生个数。(其实遍历不了这么多)
    • 空间复杂度 O ( n ) O(n) O(n)

    AC代码

    C++
    class Solution {
    public:
        int countStudents(vector<int>& students, vector<int>& sandwiches) {
            queue<int> q;
            for (int& t : students) {
                q.push(t);
            }
            stack<int> st;
            for (int i = sandwiches.size() - 1; i >= 0; i--) {
                st.push(sandwiches[i]);
            }
            while (true) {
                int thisSandwich = st.top();
                st.pop();
                bool found = false;
                for (int i = q.size(); i > 0; i--) {
                    int thisStudent = q.front();
                    q.pop();
                    if (thisStudent == thisSandwich) {
                        found = true;
                        break;
                    }
                    else {
                        q.push(thisStudent);
                    }
                }
                if (!found) {
                    return q.size();
                }
                else if (q.empty()) {
                    return 0;
                }
            }
            return -1;  // Fake Return
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    方法二:假模拟

    真的要学生一个一个地出队入队吗?

    当然不!假如栈顶三明治是1,那么只要队列中存在1就能匹配上啊

    谁先匹配上的不影响结果。

    除非剩下学生全是0😉,那所有人都吃不到了。

    打住,刚刚说什么,“剩下学生全是0”?

    哦哦,这不就是终止条件嘛!

    我们只需要从前到后遍历三明治(模拟出栈的过程),如果有学生与这个三明治匹配,那就拿走去吃,否则(所有学生与三明治都不匹配),模拟终止,谁都吃不到了~~(论1的重要性)~~

    如果三明治遍历完了,那就说明所有同学都吃到了,那就返回0

    • 时间复杂度 O ( n ) O(n) O(n),其中 n n n是学生个数
    • 空间复杂度 O ( 1 ) O(1) O(1)

    AC代码

    C++
    class Solution {
    public:
        int countStudents(vector<int>& students, vector<int>& sandwiches) {
            // s[0]代表学生中0的数量,s[1]代表学生中1的数量
            int s[2] = {(int)count(students.begin(), students.end(), 0), (int)students.size() - s[0]};
            // cout << s[0] << ' ' << s[1] << endl;
            for (int& t : sandwiches) {
                if (s[t])  // 匹配
                    s[t]--;  // 走人
                else  // 无人可匹
                    return s[0] + s[1];  // 谁都别想吃了
            }
            return 0;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意,这里是学生0三明治0匹配,不是01匹配。

    代码简化(行数压缩)

    class Solution {
    public:
        int countStudents(vector<int>& students, vector<int>& sandwiches) {
            int s[2] = {(int)count(students.begin(), students.end(), 0), (int)students.size() - s[0]};
            for (int& t : sandwiches)
                if (s[t]) s[t]--;
                else return s[0] + s[1];
            return 0;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法三:奇技淫巧 - 计时器

    方法三对应于方法一,也是真模拟。

    不同之处在于方法一中,我们需要判断“是否所有学生都出队过一次”

    不同的是,方法三中,没有对此进行判断,而是当没有学生能与栈顶三明治匹配时,不断地进行“出队入队出队入队出队入队…”

    直到把学生累死,查看尸体个数就行了。

    怎么累死呢?

    我们在程序中设置一个计时器,对于100个学生这种数量级,一般几毫秒就能模拟完。(我们把几毫秒看成是“午饭时间30min”)

    那么好,我们执行个“1000毫秒”,1000ms / 5ms * 30min = 60,000min = 1000h = 41.666…天

    让所有学生不吃一口三明治不断排队40多天,肯定累死了。

    那么,剩下的学生就是答案

    • 时间复杂度:不易衡量。如果所有学生都能吃完,那么就是 O ( n 2 ) O(n^2) O(n2),其中 n n n是学生个数;如果有学生不能吃到,那程序就会执行大约1秒
    • 空间复杂度 O ( n ) O(n) O(n)

    AC代码

    C++
    class Solution {
    public:
        int countStudents(vector<int>& students, vector<int>& sandwiches) {
    		// 构建队列和栈
            queue<int> q;
            for (int& t : students) {
                q.push(t);
            }
            stack<int> st;
            for (int i = sandwiches.size() - 1; i >= 0; i--) {
                st.push(sandwiches[i]);
            }
    		开始模拟
            time_t start = clock();
            while (clock() - start < 1000 && q.size()) {
                if (q.front() == st.top())
                    q.pop(), st.pop();
                else {
                    int thisStudent = q.front();
                    q.pop();
                    q.push(thisStudent);
                }
            }
            return q.size();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    同步发文于CSDN,原创不易,转载请附上原文链接哦~
    Tisfy:https://letmefly.blog.csdn.net/article/details/127402719

  • 相关阅读:
    每日一题之原子的数量
    numpy对行操作总结
    如何配置ESB单据集成接口
    Revit导入Cad图元丢失不正确解决和链接CAD功能
    在Linux系统上检测GPU显存和使用情况
    flutter单独页面修改状态栏不生效问题
    大数据可视化的设计规范,全面剖析,很实用。
    神经系统图 基本结构图,神经系统的组织结构图
    学位类型有哪几种,专业学位和学术型学位区别
    OPT(奥普特)荣摘高工锂电“2022年度创新技术奖”
  • 原文地址:https://blog.csdn.net/Tisfy/article/details/127402719