LeetCode第 188 场周赛(C++)

1、用栈操作构建数组(3分)

题目描述

给你一个目标数组 target和一个整数n。每次迭代,需要从 list = {1,2,3..., n}中依序读取一个数字。

请使用下述操作来构建目标数组target

  • Push:从 list 中读取一个新元素, 并将其推入数组中。
  • Pop:删除数组中的最后一个元素。
  • 如果目标数组构建完成,就停止读取更多元素。

题目数据保证目标数组严格递增,并且只包含1n之间的数字。
请返回构建目标数组所用的操作序列。
题目数据保证答案是唯一的。

示例 1:

输入:target = [1,3], n = 3
输出:["Push","Push","Pop","Push"]
解释: 
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]

示例 2:

输入:target = [1,2,3], n = 3
输出:["Push","Push","Push"]

示例 3:

输入:target = [1,2], n = 4
输出:["Push","Push"]
解释:只需要读取前 2 个数字就可以停止。

示例 4:

输入:target = [2,3,4], n = 4
输出:["Push","Pop","Push","Push","Push"]

提示:

1 <= target.length <= 100
1 <= target[i] <= 100
1 <= n <= 100
target 是严格递增的

解题思路

  • 题目中所给的n值是没有用的,记住target数组的最后一个值即可。
  • 分两种情况,(1)target[i]等于list[i]所对应的值,此时执行Push操作即可;(2)target[i]所对应的值大于相应的list[i]的值,说明有出栈的数字,此时依次执行Push,Pop直到target[i]list[i]的值相对应即可。

代码实现

class Solution {
public:
    vector<string> buildArray(vector<int>& target, int n) {
        vector<string> svec;
        int count = 1;
        for(int i = 0; i < target.size(); i++)
        {
            while(target[i] > count)
            {
                svec.push_back("Push");
                svec.push_back("Pop");
                count++;
            }
            if(target[i] == count)
            {
                svec.push_back("Push");
                count++;
            }
        }
        return svec;   
    }
};

运行截图

在这里插入图片描述


2、形成两个异或相等数组的三元组数目(4分)

题目描述

给你一个整数数组 arr

现需要从数组中取三个下标ijk,其中(0 <= i < j <= k < arr.length)

ab定义如下:

  • a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]
  • b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]

注意:^表示 按位异或 操作。

请返回能够令a == b成立的三元组 (i, j , k)的数目。

示例 1:

输入:arr = [2,3,1,6,7]
输出:4
解释:满足题意的三元组分别是 (0,1,2), (0,2,2), (2,3,4) 以及 (2,4,4)

示例 2:

输入:arr = [1,1,1,1,1]
输出:10

示例 3:

输入:arr = [2,3]
输出:0

示例 4:

输入:arr = [1,3,5,7,9]
输出:3

示例 5:

输入:arr = [7,11,12,9,5,2,7,17,22]
输出:8

提示:

1 <= arr.length <= 300
1 <= arr[i] <= 10^8

解题思路

  • 直接使用暴力求解,三层for循环取遍ijk所在位置的所有情况。
  • C++中使用运算符^可以直接实现两个十进制数的按位异或运算。

代码实现

class Solution {
public:
    int countTriplets(vector<int>& arr) {
        if(arr.size() < 2)
            return 0;
        if(arr.size() == 2)
            return arr[0] == arr[1] ? 1 : 0;
        int count = 0;
        for(int i = 0; i < arr.size(); i++)
        {
            int num1 = arr[i];
            for(int j = i + 1; j < arr.size(); j++)
            {
                int num2 = 0;
                for(int k = j; k < arr.size(); k++)
                {
                    num2 ^= arr[k];
                    if(num1 == num2)
                        count++;
                }
                num1 ^= arr[j];
            }
        }
        return count;
    }
};

运行截图

在这里插入图片描述


3、收集树上所有苹果的最少时间(5分)

题目描述

给你一棵有n个节点的无向树,节点编号为0n-1,它们中有一些节点有苹果。通过树上的一条边,需要花费1秒钟。你从节点0出发,请你返回最少需要多少秒,可以收集到所有苹果,并回到节点0

无向树的边由 edges给出,其中 e d g e s [ i ] = [ f r o m i , t o i ] edges[i] = [from_i, to_i] edges[i]=[fromi,toi],表示有一条边连接 f r o m i from_i fromi t o i to_i toi。除此以外,还有一个布尔数组 hasApple,其中 hasApple[i] = true代表节点i有一个苹果,否则,节点i没有苹果。

示例 1:

在这里插入图片描述

输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], 
hasApple = [false,false,true,false,true,true,false]
输出:8 
解释:上图展示了给定的树,其中红色节点表示有苹果。
一个能收集到所有苹果的最优方案由绿色箭头表示。

示例 2:

在这里插入图片描述

输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], 
hasApple = [false,false,true,false,false,true,false]
输出:6
解释:上图展示了给定的树,其中红色节点表示有苹果。
一个能收集到所有苹果的最优方案由绿色箭头表示。

示例 3:

输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], 
hasApple = [false,false,false,false,false,false,false]
输出:0

提示:

1 <= n <= 10^5
edges.length == n-1
edges[i].length == 2
0 <= fromi, toi <= n-1
fromi < toi
hasApple.length == n

解题思路

  • 要收集到每个苹果,最基本的方法就是每个有苹果的结点遍历一次,如果某些节点本身没有苹果并且其子树没有苹果的结点,无需遍历。
  • 统计到所有有苹果的结点,依次遍历,就是其最佳找苹果的路径。
  • 本质方法就是使用图的深度优先遍历算法,但是在解答的过程中遇到了问题,对于这样的例子:[0,2],[0,3],[1,2]、[false,true,false,false]显然是一个“有向”图,即结点0和结点1均指向结点2,如果还是用上述图的深度优先遍历算法并将结点本身或者以该结点为根的子树没有苹果的结点丢弃的方法,就会出现问题。对于该例,从结点0开始遍历,会发现结点0,结点2,结点3均符合丢弃的情况,但本例的真正含义是从0到2再到1再返回,是4步,显然不合题意,但是网上的很多“标准答案”都是这样写的。
  • 我的思路是定义一个路径反转函数,如果之前的路径里面的弧头是其余路径的弧尾,就将其转置。但这样做又会对100000个例子的情况就会超时,所以这里就有了一个技巧,在路径较少的时候调用了这个函数。

代码实现

class Solution {
public:
    void reverseedge(vector<vector<int>>& edges) 
    {// 路径反转函数 每一次都调用的话 会超出时间限制 
        if(edges.size() == 1)
            return;
        for(int i = 0; i < edges.size(); i++)
            for(int j = i + 1; j < edges.size(); j++)
                if(edges[i][1] == edges[j][1])
                    {
                        int temp = 0;
                        temp = edges[j][0];
                        edges[j][0] = edges[j][1];
                        edges[j][1] = temp;
                    }
    }
    bool DFS_FindAppleNode(int inode, vector<bool>& hasApple)
    {   // 属于无向图的深度优先遍历
        bool nodehasApple = hasApple[inode];
        for(auto& i : iumap[inode])
            nodehasApple = DFS_FindAppleNode(i, hasApple) or nodehasApple;
        if(nodehasApple == false)
            iumap.erase(inode);
        return nodehasApple;
    }
    int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
        if(edges.size() < 100) // 使用了一个技巧 在数量比较小的时候调用
            reverseedge(edges); // 结果就AC了
        for(auto& edge : edges)
            iumap[edge[0]].push_back(edge[1]);            
        DFS_FindAppleNode(0, hasApple);
        return iumap.size() == 0 ? 0 : 2 * (int)(iumap.size() - 1);
    }

private:
    unordered_map<int, vector<int>> iumap;
    
};

所以该代码需要进一步改进!!!


4、切披萨的方案数(7分)

问题描述

给你一个 r o w s × c o l s rows\times{cols} rows×cols 大小的矩形披萨和一个整数k,矩形包含两种字符:'A'(表示苹果)和'.'(表示空白格子)。你需要切披萨k-1次,得到 k块披萨并送给别人。

切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。

请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对10^9 + 7取余的结果。

示例 1:
在这里插入图片描述

输入:pizza = ["A..","AAA","..."], k = 3
输出:3 
解释:上图展示了三种切披萨的方案。注意每一块披萨都至少包含一个苹果。

示例 2:

输入:pizza = ["A..","AA.","..."], k = 3
输出:1

示例 3:

输入:pizza = ["A..","A..","..."], k = 1
输出:1

提示:

1 <= rows, cols <= 50
rows == pizza.length
cols == pizza[i].length
1 <= k <= 10
pizza 只包含字符 'A' 和 '.' 

解题思路

代码实现

#define ll long long int

class Solution {
public:

    const ll mod=1e9+7;
    int ways(vector<string>& pizza, int k) {
        int row=pizza.size(),col=pizza[0].length();
        //计算num
        vector<vector<int>> num(row,vector<int>(col,0));
        if(pizza[0][0]=='A') num[0][0]=1;
        for(int i=1;i<row;i++) num[i][0]=num[i-1][0]+(pizza[i][0]=='A');
        for(int i=1;i<col;i++) num[0][i]=num[0][i-1]+(pizza[0][i]=='A');
        for(int i=1;i<row;i++) for(int j=1;j<col;j++)
                num[i][j]=num[i-1][j]+num[i][j-1]-num[i-1][j-1]+(pizza[i][j]=='A');
            
        //初始化dp
        vector<vector<vector<ll>>> dp(row,vector<vector<ll>>(col,vector<ll>(k+1,0)));
        dp[0][0][1]=1;

        //从k=2开始填充
        for(int x=2;x<=k;x++){
            for(int i=0;i<row;i++){
                for(int j=0;j<col;j++){
                    //dp为0代表不存在这种情况
                    if(dp[i][j][x-1]==0)
                        continue;
                    //穷举水平切
                    for(int z=i+1;z<row;z++){
                        if(hasA(num,i,j,z-1,col-1) && hasA(num,z,j,row-1,col-1)){
                            dp[z][j][x]+=dp[i][j][x-1];
                            dp[z][j][x]%=mod;
                        }
                    }
                    //穷举垂直切
                    for(int z=j+1;z<col;z++){
                        if(hasA(num,i,j,row-1,z-1) && hasA(num,i,z,row-1,col-1)){
                            dp[i][z][x]+=dp[i][j][x-1];
                           dp[i][z][x]%=mod;
                        }
                    }
                }
            }
        }
            
        //计算答案
        ll ans=0;
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                ans+=dp[i][j][k];
            }
            ans%=mod;
        }
        return ans;
    }
        
    //计算存在A吗
    bool hasA(vector<vector<int>>& num,int sr,int sc,int er,int ec){
        int num1=0,num2=0,num3=0,res;
        if(sr!=0 && sc!=0) num1=num[sr-1][sc-1];
        if(sr!=0) num2=num[sr-1][ec];
        if(sc!=0) num3=num[er][sc-1];
        return num[er][ec]-num2-num3+num1>0;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值