Leetcode 第147场周赛题目学习

1137. 第 N 个泰波那契数

题目描述

泰波那契序列 Tn 定义如下:

T 0 = 0 , T 1 = 1 , T 2 = 1 , 且 在 n > = 0 的 条 件 下 T n + 3 = T n + T n + 1 + T n + 2 T_{0} = 0, T_{1} = 1, T_{2} = 1, 且在 n >= 0 的条件下 T_{n+3} = T{_n} + T_{n+1} + T_{n+2} T0=0,T1=1,T2=1,n>=0Tn+3=Tn+Tn+1+Tn+2

给你整数 n,请返回第 n 个泰波那契数 T n T_{n} Tn的值。
示例 1:

输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

示例 2:

输入:n = 25
输出:1389537

提示:

  • 0 <= n <= 37
  • 答案保证是一个 32 位整数,即 answer <= 2^31 - 1

参考思路

与求解斐波那契数列的第n项的思路相同,采用递推的方式进行求解。
需要注意的是,答案是一个32位整数,因此需要用unsigned int存储答案,如果用int存储,可能会发生越界。

参考代码

class Solution {
public:
    int tribonacci(int n) 
    {
        if(n==0)
            return 0;
        if(n==1)
            return 1;
        if(n==2)
            return 1;
        unsigned int pre=0,mid=1,post=1;
        unsigned int ans;
        for(int i=3;i<=n;++i)
        {
            ans=pre+mid+post;
            pre=mid;
            mid=post;
            post=ans;
        }
        return ans;
    }
};

1138.字母板上的路径

题目描述

我们从一块字母板上的位置 (0, 0) 出发,该坐标对应的字符为 board[0][0]

在本题里,字母板为board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"].

我们可以按下面的指令规则行动:

  • 如果方格存在,'U' 意味着将我们的位置上移一行;
  • 如果方格存在,'D' 意味着将我们的位置下移一行;
  • 如果方格存在,'L' 意味着将我们的位置左移一列;
  • 如果方格存在,'R' 意味着将我们的位置右移一列;
  • ‘!’ 会把在我们当前位置 (r, c) 的字符 board[r][c] 添加到答案中。

返回指令序列,用最小的行动次数让答案和目标 target 相同。你可以返回任何达成目标的路径。

示例 1:

输入:target = "leet"
输出:"DDR!UURRR!!DDD!"

示例 2:

输入:target = "code"
输出:"RR!DDRR!UUL!R!"

提示:

  • 1 <= target.length <= 100
  • target 仅含有小写英文字母。

参考思路

由于仅能上下左右移动,实际上给定单词的指令序列长度是一定的。而且题目中并未规定输出指令的顺序,因此只要明确两个字母间的横向和纵向距离差即可输出相应的指令。
由于字母板最后一行仅有一个字母z,因此若想移动到z,仅能从第5行的u向下移动到z。若想从z移动到其他字母,只能先从z开始向上移动,随后再进行左右移动。因此,若目的地是z,则先执行左右移动的指令,再执行上下移动的指令;若起点是z,则先执行左右移动的指令,再执行上下移动的指令。
本题中无需构建出字母板,只需要计算各字母间坐标,求出目标点和起始点之间的坐标的相对大小即可:

int xi = (x - 'a') / 5;
int xj = (x - 'a') % 5;
int curi = (cur - 'a') / 5;
int curj = (cur - 'a') % 5;
deltai = xi - curi;
deltaj = xj - curj;

若偏移量大于0,则执行下/右移动,若偏移量小于0,则执行上/左移动。

参考代码

char row[2]={'U','D'};
char col[2]={'L','R'};

class Solution {
public:
    string alphabetBoardPath(string target)
	{
		char cur = 'a';
		string ans;
		int deltai, deltaj;
		for (auto x : target)
		{
			calculate(x, cur,deltai,deltaj);
            if (cur == 'z')
			{
				for (int i = 0; i<abs(deltai); ++i)
					ans += row[deltai>0];
				for (int j = 0; j<abs(deltaj); ++j)
					ans += col[deltaj>0];
			}
			else
			{
				for (int j = 0; j<abs(deltaj); ++j)
					ans += col[deltaj>0];
				for (int i = 0; i<abs(deltai); ++i)
					ans += row[deltai>0];
			}
			ans += '!';
			cur = x;
		}
		return ans;
	}
	void calculate(char x, char cur, int& deltai, int&deltaj)
	{
		int xi = (x - 'a') / 5;
		int xj = (x - 'a') % 5;
		int curi = (cur - 'a') / 5;
		int curj = (cur - 'a') % 5;
		deltai = xi - curi;
		deltaj = xj - curj;
	}
};

1139.最大的以1位边界的正方形

题目描述

给你一个由若干 01 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0
示例 1:

输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:9

示例 2:

输入:grid = [[1,1,0,0]]
输出:1

提示:

  • 1 <= grid.length <= 100
  • 1 <= grid[0].length <= 100
  • grid[i][j] 为 0 或 1

参考思路

本题可利用滑动窗口法进行检测。具体过程结合参考代码分析。

参考代码

class Solution {
public:
    int largest1BorderedSquare(vector<vector<int>>& g) 
	{
        int s1[105][105],s2[105][105],ans=0;
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        int n=g.size(),m=g[0].size(),i,j,k;
        for (i=1;i<=n;i++)
        {
			for (j=1;j<=m;j++)
			{
				s1[i][j]=s1[i][j-1]+g[i-1][j-1];//s1是每行的积累和
				s2[i][j]=s2[i-1][j]+g[i-1][j-1];//s2是每列的积累和
			}
        }
		int len=min(m,n);
        for (i=1;i<=n;i++)
        {
			for (j=1;j<=m;j++)
			{
				for (k=1;i+k-1<=n&&j+k-1<=m;k++)//(i,j)是正方形滑窗的左上角的坐标,k是滑窗大小
				{
					int l1=i,r1=i+k-1;
					int l2=j,r2=j+k-1;
					if (k<=ans) continue;//当前边长小于已经获得的边长,则直接跳过
					if (s1[l1][r2]-s1[l1][l2-1]!=k) continue;
					if (s1[r1][r2]-s1[r1][l2-1]!=k) continue;
					if (s2[r1][r2]-s2[l1-1][r2]!=k) continue;
					if (s2[r1][l2]-s2[l1-1][l2]!=k) continue;
					ans=k;
					if(ans==len)
						return ans*ans;
				}
			}
        }
        return ans*ans;
    }
};

分析:
s1和s2分别是每行元素的累积和与每列元素的累积和。若当前行全为1,则累积和的最后一个元素应等于列的数目。
随后进行滑窗检测。滑窗左上角的起始点为(i,j),滑窗的边长为k。根据起点和k可以得出行的起点和终点为[l1,r1],列的起点和终点为[l2,r2]。随后进行5项检测:

  1. 当前滑窗边长小于已经获得的边长,则无需进行此次检验
  2. 当前最上边的累积和不等于滑窗边长k,则跳过
  3. 当前最下边的累积和不等于滑窗边长k,则跳过
  4. 当前最左边的累积和不等于滑窗边长k,则跳过
  5. 当前最右边的累积和不等于滑窗边长k,则跳过
  6. 如果当前获得的结果等于网格的长宽中最小值,说明已经获得最大网格,则可直接退出检测。

1140. 石子游戏 II

题目描述

亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]。游戏以谁手中的石子最多来决出胜负。

亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1

在每个玩家的回合中,该玩家可以拿走剩下的 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)

游戏一直持续到所有石子都被拿走。

假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。

示例:

输入:piles = [2,7,9,4,4]
输出:10
解释:
如果亚历克斯在开始时拿走一堆石子,李拿走两堆,接着亚历克斯也拿走两堆。在这种情况下,亚历克斯可以拿到 2 + 4 + 4 = 10 颗石子。 
如果亚历克斯在开始时拿走两堆石子,那么李就可以拿走剩下全部三堆石子。在这种情况下,亚历克斯可以拿到 2 + 7 = 9 颗石子。
所以我们返回更大的 10。 

提示:

  • 1 <= piles.length <= 100
  • 1 <= piles[i] <= 10 ^ 4

参考思路

(暂时不懂)

参考代码

第一种方法(不理解其动态规划)
class Solution {
public:
    int stoneGameII(vector<int>& piles) 
	{
        int dp[105][105],s[105],n=piles.size(),i,j,k;
        memset(dp,0,sizeof(dp));
        memset(s,0,sizeof(s));
        for (i=1;i<=n;i++) 
			s[i]=s[i-1]+piles[i-1];
        for (i=n;i>=1;i--)
        {
			for (j=1;j<=100;j++)
			{
				for (k=1;i+k-1<=n&&k<=2*j;k++)
					dp[i][j]=max(dp[i][j],s[n]-s[i-1]-dp[i+k][min(100,max(j,k))]);
			}
        }
        return dp[1][1];
    }
};
第二种方法
class Solution
{
public:
	int stoneGameII(vector<int>& plies)
	{
		int len=piles.size();
		vector<vector<int> > dp(len,veector<int>(len,0));
		vector<int> sum(len,0);
		//逆序部分和
		sum.back()=piles.back();
		for(int i=len-2;i>=0;--i)
			sum[i]=sum[i+1]+piles[i];
		
		//倒着查找,dp[i][j]表示当前在i位置,且此时m=j+1时可以获得的最大收益
		for(int i=len-1;i>=0;--i)
			for(int j=0;j<len;++j)
			{
				//剩下的点可以一次拿完
				int M=j+1;
				if(2*M>=len-i)
					dp[i][j]=sum[i];
				else
				{
					//一次拿不完的情况,取最大值
					for(int k=1;k<=2*M;++k)
						dp[i][j]=max(dp[i][j],sum[i]-dp[i+k][max(k,M)-1]);
				}
			}
		return dp[0][0];
	}
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: LeetCode是一种流行的在线刷题平台,提供了许多算法和数据结构挑战,旨在帮助程序员和计算机科学家提高他们的编程技能。以下是几个LeetCode题目及其答案: 1. 两数之和 题目描述:给定一个整数数组和一个目标值,找出数组中的两个数,它们相加等于目标值。假设每个输入只对应一种答案,并且不能使用相同的元素。 答案:遍历数组,将每个数字存储到一个哈希表中,同时计算出目标值与当前数字的差值。如果差值在哈希表中,则返回这两个数字的索引。时间复杂度为O(n)。 2. 盛最多水的容器 题目描述:给定n个非负整数a1,a2,...,an,其中每个点(i,ai)表示一个坐标。画n条垂直线,使得线i的两个端点在(i,ai)和(i,0)上。找到两条线,它们与x轴一起构成一个容器,使得容器中包含最多水。 答案:使用双指针法,分别指向数组的头部和尾部。每次计算当前容器的大小,然后根据容器两侧的高度差决定是否移动指针。时间复杂度为O(n)。 3. 无重复字符的最长子串 题目描述:给定一个字符串,请找出其中不含有重复字符的最长子串的长度。 答案:使用双指针法,分别指向子串的起始和结束位置。每次判断当前字符是否存在于子串中,如果存在,则移动起始指针到重复字符的下一个位置,否则移动结束指针。同时记录最长子串的长度。时间复杂度为O(n)。 上述题目只是LeetCode中的部分题目,不同的题目需要采用不同的算法和数据结构进行解决。通过刷LeetCode和其他类似的在线刷题平台,可以提高编程能力和算法思维能力,使得程序员更加熟练地处理各种算法问题。 ### 回答2: LeetCode一个在线的编程题库,提供了各种不同难度和类型的编程题目。这些题目主要涵盖了算法、数据结构、字符串操作、图算法等方面的内容。 每道题目都有一个独特的编号和描述,通过阅读题目可以了解到输入和输出的形式,以及题目的具体要求。解答题目通常需要理解问题的本质,设计相应的算法,编写代码实现,并进行测试验证。 题目的难度分为简单、中等和困难三个级别,根据不同的难度以及自己的编程能力,可以选择相应的题目来挑战。 解答题目的关键在于理解问题的本质,分析解决问题的思路,并设计合适的算法实现。这个过程需要对常见的算法和数据结构有一定的了解,比如动态规划、贪心算法、递归、队列等。 对于每一道题目LeetCode提供了多种语言的代码模板,如C++、Java、Python等,可以根据自己的编程习惯和喜好选择相应的语言进行代码编写。 解答题目的过程中需要注意代码的效率和正确性,特别是对于大规模输入的情况,需要考虑时间和空间复杂度。在完成代码编写后,还需要进行测试验证,确保程序的正确性。 在解答题目过程中,可以参考其他人的解题思路和代码实现,并进行对比和学习LeetCode还提供了题目的讨论区,可以与其他的编程爱好者交流和分享经验。 总之,通过LeetCode可以提升自己的编程能力,加深对算法和数据结构的理解,并为面试和竞赛积累经验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值