LeetCode Week18: Sicily Test

这周完成的是老师给定的期末考试模拟题,这里也选择这些题目来写题解,顺便回顾一下这学期的知识。

1000.

题目

定义超级和函数F如下:
F(0, n) = n,对于所有的正整数n..
F(k, n) = F(k – 1, 1) + F(k – 1, 2) + … + F(k – 1, n),对于所有的正整数k和n.

请实现下面Solution类中计算F(k, n)的函数(1 <= k, n <= 14).

class Solution {
public:
     int F(int k, int n) {

     }
};

例1:F(1, 3) = 6

例2:F(2, 3) = 10

例3:F(10, 10) = 167960

注意:你只需要提交Solution类的代码,你在本地可以编写main函数测试程序,但不需要提交main函数的代码. 注意不要修改类和函数的名称.

我的分析

这道题直接用动态规划即可求解,即dp[i][j]表示第i行第j个元素的值,这个值是前面i-1行的前j个元素的值,而dp[i-1][j]是第i-1行的前j-1个元素的值,所以dp[i][j] = dp[i-1][j] + dp[i][j-1]。最终返回dp[k][n]即可。

代码

class Solution {
public:
    int F(int k, int n) {
        vector<vector<int> >dp(k+1, vector<int>(n, 0));
        for(int i = 0; i <= k; i++) dp[i][0] = 1;
        for(int j = 0; j < n; j++) dp[0][j] = j+1;
        for(int i = 1; i <= k; i++)
            for(int j = 1; j < n; j++)
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
        return dp[k][n-1];
    }
};

1001.

题目

N个会议要同时举行,参会人数分别为A[0], A[1], …, A[N-1]. 现有M个会议室,会议室可容纳人数分别为B[0], B[1], …, B[M-1]. 当A[i]<=B[j]时,可以把会议i安排在会议室j,每间会议室最多安排一个会议,每个会议最多只能安排一个会议室. 求最多安排多少个会议.

1 <= N, M <= 100000, 每个会议的参会人数和每间会议室的容纳人数均在1和1000之间.

请为下面的Solution类实现解决上述问题的函数assignConferenceRoom. 函数参数A和B的意义如上,返回值为最多可安排的会议数.

class Solution {
public:
    int assignConferenceRoom(vector<int>& A, vector<int>& B) {

    }
};

例1:A={2, 3}, B={1, 2},答案为1.

例2:A={3, 4, 5},B={10, 3, 2},答案为2.

注意:你只需要提交Solution类的代码,你在本地可以编写main函数测试程序,但不需要提交main函数的代码. 注意不要修改类和函数的名称.

我的分析

利用贪心算法可以解决这个题目,对于对两个数组都先排序。之后利用贪心算法,从容纳人数最多的会议室开始分配,先给会议室分配能最大化利用它容量的会议,即会议的人数不大于容纳人数且是剩余的满足条件的会议中人数最多的,直到把所有的会议室和会议都遍历结束即停止。

代码

class Solution {
public:
    int assignConferenceRoom(vector<int>& A, vector<int>& B) {
          sort(A.begin(), A.end());
          sort(B.begin(), B.end());
          int count = 0;
          int idx = B.size() - 1;
          for(int j = A.size() - 1; j >= 0; j--)
            if(B[idx] >= A[j]){
                count += 1;
                idx--;
            }
          return count;
    }
};

1002.

题目

两个二叉树结构相同,且对应结点的值相同,我们称这两个二叉树等价.

例如:以下两个二叉树等价

    1           1
   / \         / \
  2   3       2   3

而以下两个则不等价

    1           1
   / \         / \
  2   3       3   2

以下两个也不等价

    1           1
   / \         / \
  2   3       2   2

给出两个二叉树p和q,判断它们是否等价.

p和q的结点数不多于100000,每个结点的数值在1和1000000000之间.

请为下面的Solution类实现解决上述问题的isEqual函数,函数的两个参数p和q分别代表两个二叉树的根节点,如果以p和q为根的二叉树等价则函数返回true,否则返回false.

/**
  Definition for a binary tree node.
  struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };
 */
class Solution {
public:
       bool isEqual(TreeNode* p, TreeNode* q) {

    }
};

我的分析

这道题使用递归即可解决,即对于两棵树,如果两棵树的左子树和右子树都相等,并且根结点的值也相等,那么这两棵树就相等。如果遍历到两个结点都为空,证明整颗子树都可以被正确遍历,不然中途就会返回false,所以此时可以返回true,而当判断过程中一颗树的子树为空而另一个不为空或者是根结点的值不同时,都会返回false。

代码

class Solution {
public:
    bool isEqual(TreeNode* p, TreeNode* q) {
        if(p == NULL && q == NULL) return true;
        if(p == NULL || q == NULL) return false;
        return (p->val == q->val) && isEqual(p->left,q->left) && isEqual(p->right,q->right); 
    }
};

1003.

题目

对于一个01矩阵A,求其中有多少片连成一片的1. 每个1可以和上下左右的1相连.

请为下面的Solution类实现解决这一问题的函数countConnectedOnes,函数参数A为给出的01矩阵,A的行数和列数均不大于1000. 函数的返回值是问题的答案.

class Solution {
public:
    int countConnectedOnes(vector<vector<char>>& A) {

    }
};

例1:
A=

100
010
001

答案为3.

例2:
A=

1101
0101
1110

答案为2.

我的分析

这一题跟LeetCode的求跟0的距离的题目类似,利用BFS可以求解。
对于每一个位置的值,如果当前值为1并且没有被遍历过(利用一个visited数组进行记录),那么就对这个数组的上下左右的值进行逐一遍历,直到遍历到的点的值为0,或者是这个点已经被遍历过,或者是遍历的范围超过矩阵范围时就停止遍历。

代码

class Solution {
public:
    int countConnectedOnes(vector<vector<char> >& grid) {
        int count = 0;
        int rmax = grid.size(), cmax = grid[0].size();
        vector<vector<bool> > visited(rmax,vector<bool>(cmax,false));
        for(int i = 0; i < rmax; i++)
            for(int j = 0; j < cmax; j++)
                if(!visited[i][j] && grid[i][j] == '1'){
                    dfshelper(grid, visited,i,j,rmax-1,cmax-1);
                    ++count;
                }
        return count;                    
    }
    void dfshelper(vector<vector<char> >& A, vector<vector<bool> >& visited, int r, int c, int rmax, int cmax){
        if(r > rmax || c > cmax || r < 0 || c < 0 || A[r][c] == '0' || visited[r][c] == true) return;
        visited[r][c] = true;
        dfshelper(A, visited,r+1,c,rmax,cmax);
        dfshelper(A, visited,r-1,c,rmax,cmax);
        dfshelper(A, visited,r,c+1,rmax,cmax);
        dfshelper(A, visited,r,c-1,rmax,cmax);
    }
};

1004.

题目

在图论中,如果一个有向图从任意顶点出发无法经过若干条边回到该点,则这个图是一个有向无环图(Directed Acyclic Graph,DAG). 对于一个n个节点的有向图(节点编号从0到n-1),请判断其是否为有向无环图.

图的节点数和边数均不多于100000.

请为下面的Solution类实现解决上述问题的isDAG函数,函数参数中n为图的节点数,edges是边集,edges[i]表示第i条边从edges[i].first指向edge[i].second. 如果是有向无环图返回true,否则返回false.

class Solution {
public:
    bool isDAG(int n, vector<pair<int, int>>& edges) {

    }
};

例1:
n = 3,edges = {(0, 1), (0, 2)},函数应返回true.

例2:
n = 2,edges = {(0, 1), (1, 0)},函数应返回false.

注意:你只需要提交Solution类的代码,你在本地可以编写main函数测试程序,但不需要提交main函数的代码. 注意不要修改类和函数的名称.

我的分析

可以利用拓扑排序来解决这个判断有向图是否有环的问题。
(1)对于所有的点,记录其领结的点,并且按照点的顺序依次排放所有的邻接点。
(2)同时记录所有的点的入度值;
(3)之后对于所有的点进行遍历,如果能找到一个入度为0的点,删掉改点并删除从此点发出的边,也就是与这个点邻接的点的入度都减一,一直重复遍历下去直到遍历完所有的结点,如果能遍历完所有的结点,那么表示无环,如果找不到入度为0的点,那么就表示有环,直接停止遍历返回false即可。

代码

class Solution {
public:
bool isDAG(int numCourses, vector

1005.

题目

Description
从数列A[0], A[1], A[2], …, A[N-1]中选若干个数,要求相邻的数不能都选,也就是说如果选了A[i], 就不能选A[i-1]和A[i+1]. 求能选出的最大和.

1 <= N <= 100000, 1 <= A[i] <= 1000

请为下面的Solution类实现解决上述问题的函数maxSum,函数参数A是给出的数列,返回值为所求的最大和.

class Solution {
public:
    int maxSum(vector<int>& A) {

    }
};

例1:A = {2, 5, 2},答案为5.

例2:A = {2, 5, 4},答案为6.

注意:你只需要提交Solution类的代码,你在本地可以编写main函数测试程序,但不需要提交main函数的代码. 注意不要修改类和函数的名称.

我的分析

这道题可以用动态规划进行求解,这一题和LeetCode上的House robber那道题内容是一样的,用dp[i]表示到位置i时,可以有的最大的和,那么dp[i]要么就是位置i-1的最大和(表明nums[i]没有被选择),要么就是位置i-2的最大和加上nums[i],即dp[i] = max(nums[i]+dp[i-2],dp[i-1]),最终返回的dp[n]就是前n项的最大和。

代码

class Solution {
public:
    int maxSum(vector<int>& A) {
        if(A.size() == 1) return A[0];
        int len = A.size();
        vector<int> dp(len,0);
        dp[0] = A[0]; dp[1] = max(dp[0],A[1]);
        for(int i = 2; i < len; i++)
            dp[i] = max(dp[i-1],dp[i-2]+A[i]);
        return dp[len-1]; 
    }
};

1006.

题目

对于两个只含有小写英文字母(’a’-‘z’)的单词word1和word2,你可以对word1进行以下3种操作:

1) 插入一个字母;
2) 删除一个字母;
3) 替换一个字母.

请计算将word1变换成word2的最少操作数.

word1和word2的长度均不大于1000.

请为下面的Solution类实现解决上述问题的函数minDistance,函数的参数word1和word2为给出的两个单词,返回值为所求最少操作数.

class Solution {
public:
    int minDistance(string word1, string word2) {

       }
};

例1:word1 = “sunny”, word2 = “snowy”,返回值为3.

例2:word1 = “abc”, word2 = “ac”,返回值为1.

注意:你只需要提交Solution类的代码,你在本地可以编写main函数测试程序,但不需要提交main函数的代码. 注意不要修改类和函数的名称.

我的分析

这一题就是动态规划的经典题,直接进行数组填充即可。dp[i][j]表示前i个word1中的子串和前j个word2中的子串的最小修改距离,dp
[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]+(word1[i] == word2[j])), 即要么是删除或者增加一个字串,要么是修改一个子串的值。

代码

class Solution {
public:
    int minDistance(string word1, string word2) {
        int len1 = word1.length(), len2 = word2.length();
        vector<vector<int> >dp(len1+1,vector<int>(len2+1,0)); 
        for(int i = 0; i <= len1; i++) dp[i][0] = i;
        for(int j = 0; j <= len2; j++) dp[0][j] = j;
        for(int i = 1; i <= len1; i++)
            for(int j = 1; j <= len2; j++)
                dp[i][j] = min(min(dp[i-1][j],dp[i][j-1])+1,((word1[i-1] == word2[j-1])?0:1)+dp[i-1][j-1]);
        return dp[len1][len2];
    }
};

1007.

题目

Description
随着共享经济的兴起,大学城如今到处可见ofo小黄车. 小左现在打算每天都骑小黄车从宿舍去实验室. 假设大学城的地图可以简化为一个有向图,图中有N个地点(节点),用0到N-1进行编号,有些地点之间存在有向的道路(有向边). 小左的宿舍所在地点编号为0,实验室所在地点编号为N-1. 小左希望为连续的M天规划线路,使得每天从宿舍到实验室,都至少会经过一条之前没有走过的道路(有向边). 小左想知道M的最大值,你能帮助他么?

请实现下面Solution类中的countPath函数,完成上述功能.
参数G: N*N(2 <= N <= 50)邻接矩阵,如果地点i到地点j之间有道路,则G[i][j] = 1;否则G[i][j] = 0. G[i][i]的值总是为0.
返回值:M的最大值. 如果不存在满足要求的路径则返回0.

class Solution {
public:
       int countPaths(vector<vector<int>> G) {

};

例1:

G = {{0, 1}, {1, 0}},返回值为2

因为第1天:0 –> 1,第2天 0 –> 1 –> 0 –> 1. 虽然小左第2天兜了一下圈,但他确实走了一条第1天没有走过的边1 –> 0.

例2:

G = {{0, 1, 1}, {1, 0, 1}, {1, 0, 0}},返回值为4.
第1天:0 –> 2
第2天:0 –> 2–> 0 –> 2
第3天:0 –> 1 –> 2
第4天:0 –> 1 –> 0 –> 1 –> 2

例3:

G = {{0, 1, 0}, {1, 0, 0}, {0, 0, 0}},返回值为0.

注意:
1. 你只需要提交Solution类的代码,你在本地可以编写main函数测试程序,但不需要提交main函数的代码.
2. 本题用近200组数据进行测试.

我的分析

因为考虑到会有返回路径的问题,所以对于这个题,其实是先寻找从指定点到终点的路径,然后来看中途点上的返回路径(即环)的一个过程。
首先对于终点n,找到能到n的所有点,并且记录下这些点的遍历情况,当遍历到起始结点时即停止遍历,这个过程可以用一个队列来实现(类似于一个BFS的过程);
之后对于点0~n-1,逐步判断点的连接情况:
(1)对于每一个点,存在一个visited变量,初始值为false,表示所有的点都还没有被遍历过;
(2)对于点i,将与其连接的点逐一加入到一个队列中,如果与i连接的点j已经被遍历过,那就表示点i和点j之间存在环,这样就会多出一条路径,那么路径数加1;如果点j还没有被遍历过,就把点j加入到队列中,并且visit[j] = false;
(3)之后不停的从队列中取出值重复(2)的操作,当取出的值是点n时,那么就表示存在一条从0到n的路径,路径数加1;
这样就可以得到最终的路径情况了。

代码

class Solution {
public:
    int countPaths(vector<vector<int> > G) {
        int numvertices = G.size();
        vector<bool> arriveN(numvertices, false);
        vector<bool> visit(numvertices, false);
        arriveN[numvertices - 1] = true;
        // 看所有点到达N-1的情况
        queue<int>bfshelper;
        bfshelper.push(numvertices - 1);
        // 从末端点回溯,找到能到达它的所有点
        while (!bfshelper.empty()){
            int idx = bfshelper.front(); bfshelper.pop();
            for(int i = 0; i < numvertices; i++)
                if(G[i][idx] == 1 && !visit[i]) {
                    arriveN[i] = true; 
                    visit[i] = true; 
                    bfshelper.push(i);
                }
        }

        bfshelper.push(0);
        visit.resize(numvertices);
        for(int i = 0; i < numvertices; i++) visit[i] = false; 

        visit[0] = true;
        int count = 0;
        while (!bfshelper.empty()){
            int idx = bfshelper.front(); bfshelper.pop();
            //cout<<"idx is "<<idx<<endl;
            if(idx == numvertices - 1) count++; // 已经遍历到了最终的顶点了
            for(int i = 0; i < numvertices; i++)
                if(G[idx][i] == 1 && arriveN[i]){
                    //cout<<"idx is "<<idx<<" i is "<<i <<endl;
                    if(!visit[i]){
                        bfshelper.push(i);
                        visit[i] = true;
                    }
                    else {
                        count++;// 表示有回溯,所以最终总的路径数要加1
                        //cout<<i<<" "<<count<<endl;
                    }
                }
        }
        return count;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值