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];
	}
};

你可能感兴趣的:(Leetcode刷题-周赛)