0%

Leetcode周赛第165场题解

周赛传送门

https://leetcode-cn.com/contest/weekly-contest-165/

5275. 找出井字棋的获胜者

题目

AB 在一个 3 x 3 的网格上玩井字棋。

井字棋游戏的规则如下:

  • 玩家轮流将棋子放在空方格 (“ “) 上。
  • 第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子。
  • “X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上。
  • 只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
  • 如果所有方块都放满棋子(不为空),游戏也会结束。
  • 游戏结束后,棋子无法再进行任何移动。

给你一个数组 moves,其中每个元素是大小为 2 的另一个数组(元素分别对应网格的行和列),它按照 AB 的行动顺序(先 AB)记录了两人各自的棋子位置。

如果游戏存在获胜者(AB),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。

你可以假设 moves有效(遵循井字棋规则),网格最初是空的,A 将先行动。

示例 1:

1
2
3
4
5
6
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:"A"
解释:"A" 获胜,他总是先走。
"X " "X " "X " "X " "X "
" " -> " " -> " X " -> " X " -> " X "
" " "O " "O " "OO " "OOX"

示例 2:

1
2
3
4
5
6
输入:moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
输出:"B"
解释:"B" 获胜。
"X " "X " "XX " "XXO" "XXO" "XXO"
" " -> " O " -> " O " -> " O " -> "XO " -> "XO "
" " " " " " " " " " "O "

示例 3:

1
2
3
4
5
6
输入:moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
输出:"Draw"
输出:由于没有办法再行动,游戏以平局结束。
"XXO"
"OOX"
"XOX"

示例 4:

1
2
3
4
5
6
输入:moves = [[0,0],[1,1]]
输出:"Pending"
解释:游戏还没有结束。
"X "
" O "
" "

提示:

  • 1 <= moves.length <= 9
  • moves[i].length == 2
  • 0 <= moves[i][j] <= 2
  • moves 里没有重复的元素。
  • moves 遵循井字棋的规则。

题解

基本上就是枚举解决,但是使用一些小操作可以稍微简化一下,就比如说一开始就手动将九宫格里填满不一样的数字(不是A和B就行),然后将九宫格开成char的直接存A和B,这样直接返回数组值就完了

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
class Solution {
public:
string tictactoe(vector<vector<int>>& moves) {
char g[9][9];
int n=0;
char c='0';
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
g[i][j]=c,c++;
for(auto x:moves){
if(n&1)
g[x[0]][x[1]]='B';
else g[x[0]][x[1]]='A';
n++;
}
string ans;
for(int i=0;i<3;i++)
if(g[i][0]==g[i][1]&&g[i][1]==g[i][2])
return ans+=g[i][0];
for(int i=0;i<3;i++)
if(g[0][i]==g[1][i]&&g[1][i]==g[2][i])
return ans+=g[0][i];
if(g[0][0]==g[1][1]&&g[1][1]==g[2][2]) return ans+=g[0][0];
if(g[0][2]==g[1][1]&&g[1][1]==g[2][0]) return ans+=g[0][2];
if(n==9) return "Draw";
else return "Pending";
}
};

5276. 不浪费原料的汉堡制作方案

题目

圣诞活动预热开始啦,汉堡店推出了全新的汉堡套餐。为了避免浪费原料,请你帮他们制定合适的制作计划。

给你两个整数 tomatoSlicescheeseSlices,分别表示番茄片和奶酪片的数目。不同汉堡的原料搭配如下:

  • 巨无霸汉堡:4 片番茄和 1 片奶酪
  • 小皇堡:2 片番茄和 1 片奶酪

请你以 [total_jumbo, total_small]([巨无霸汉堡总数,小皇堡总数])的格式返回恰当的制作方案,使得剩下的番茄片 tomatoSlices 和奶酪片 cheeseSlices 的数量都是 0

如果无法使剩下的番茄片 tomatoSlices 和奶酪片 cheeseSlices 的数量为 0,就请返回 []

示例 1:

1
2
3
输入:tomatoSlices = 16, cheeseSlices = 7
输出:[1,6]
解释:制作 1 个巨无霸汉堡和 6 个小皇堡需要 4*1 + 2*6 = 16 片番茄和 1 + 6 = 7 片奶酪。不会剩下原料。

示例 2:

1
2
3
输入:tomatoSlices = 17, cheeseSlices = 4
输出:[]
解释:只制作小皇堡和巨无霸汉堡无法用光全部原料。

示例 3:

1
2
3
输入:tomatoSlices = 4, cheeseSlices = 17
输出:[]
解释:制作 1 个巨无霸汉堡会剩下 16 片奶酪,制作 2 个小皇堡会剩下 15 片奶酪。

示例 4:

1
2
输入:tomatoSlices = 0, cheeseSlices = 0
输出:[0,0]

示例 5:

1
2
输入:tomatoSlices = 2, cheeseSlices = 1
输出:[0,1]

提示:

  • 0 <= tomatoSlices <= 10^7
  • 0 <= cheeseSlices <= 10^7

题解

鸡兔同笼问题啊,就是解一个二元一次方程组

  • 4x+2y=a
  • x+y=b

限制就是x和y必须为整数且大于等于0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
vector<int> numOfBurgers(int a, int b) {
int t=a-b;
if((a-2*b)%2) return vector<int>();
int x=(a-2*b)/2;
if(x<0) return vector<int>();
if((a-4*x)%2) return vector<int>();
int y=(a-4*x)/2;
if(y<0) return vector<int>();
if(x+y!=b) return vector<int>();
return vector<int>({x,y});
}
};

5277. 统计全为 1 的正方形子矩阵

题目

给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
12
输入:matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
输出:15
解释:
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.

示例 2:

1
2
3
4
5
6
7
8
9
10
11
输入:matrix = 
[
[1,0,1],
[1,1,0],
[1,1,0]
]
输出:7
解释:
边长为 1 的正方形有 6 个。
边长为 2 的正方形有 1 个。
正方形的总数 = 6 + 1 = 7.

提示:

  • 1 <= arr.length <= 300
  • 1 <= arr[0].length <= 300
  • 0 <= arr[i][j] <= 1

题解

一道二维前缀和的暴力题

贴两个公式

求和的:

s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1]

求子矩阵(x1,y1),(x2,y2):

sum=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]

枚举正方形边长即可

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
class Solution {
public:
int s[500][500];
int countSquares(vector<vector<int>>& g) {
int n=g.size(),m=g[0].size();
int ans=0;
memset(s,0,sizeof s);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
s[i][j]=g[i-1][j-1];
if(s[i][j]==1) ans++;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
int d=min(n,m);
for(int len=2;len<=d;len++){
for(int i=1;i+len-1<=n;i++)
for(int j=1;j+len-1<=m;j++)
if(s[i+len-1][j+len-1]+s[i-1][j-1]-s[i-1][j+len-1]-s[i+len-1][j-1]==len*len)
ans++;
}
return ans;
}
};

后面放上dp的思路,开一个二维数组,先将i=0和j=0的一行和一列复制到dp数组里,然后我们的转移方程是dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
int countSquares(vector<vector<int>>& g) {
int n=g.size(),m=g[0].size();
vector<vector<int>> dp(n+10,vector<int>(m+10,0));
int ans=0;
for(int i=0;i<n;i++)
if(g[i][0])
dp[i][0]=1,ans++;
for(int i=0;i<m;i++)
if(i!=0&&g[0][i])
dp[0][i]=1,ans++;
for(int i=1;i<n;i++)
for(int j=1;j<m;j++)
if(g[i][j]){
dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
ans+=dp[i][j];
}
return ans;

}
};

5278. 分割回文串 III

题目

给你一个由小写字母组成的字符串 s,和一个整数 k

请你按下面的要求分割字符串:

  • 首先,你可以将 s 中的部分字符修改为其他的小写英文字母。
  • 接着,你需要把 s 分割成 k 个非空且不相交的子串,并且每个子串都是回文串。

请返回以这种方式分割字符串所需修改的最少字符数。

示例 1:

1
2
3
输入:s = "abc", k = 2
输出:1
解释:你可以把字符串分割成 "ab""c",并修改 "ab" 中的 1 个字符,将它变成回文串。

示例 2:

1
2
3
输入:s = "aabbc", k = 3
输出:0
解释:你可以把字符串分割成 "aa""bb""c",它们都是回文串。

示例 3:

1
2
输入:s = "leetcode", k = 8
输出:0

提示:

  • 1 <= k <= s.length <= 100
  • s 中只含有小写英文字母。

题解

这个题是一个比较经典的动态规划问题,首先我们做一些初始化操作,O(n^2)将每个区间转化成回文串的步数算出来,然后我们开始思考如何DP

首先我们确定dp的状态表示,dp[i][x]表示0~i的子串切分成x个回文串所需要的操作数,那么我们的dp[i][x]可以从dp[j][x-1]+val[j+1][i]转移过来,也就是在0~i之间切一刀,那么前面的dp[j][x-1]我们已经算过了,val[j+1][i]就是前面初始化操作的记录,我们只需要枚举切分点j,取一个最优的切分点使得操作数最少即可

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
class Solution {
public:
int val[200][200],dp[200][200];
void cal(const string& s,int l,int r){
int n=s.length();
if(n==1){
return;
}
int i=0,j=n-1;
while(i<j){
if(s[i]!=s[j])
val[l][r]++;
i++,j--;
}
}
int palindromePartition(string s, int k) {
int n=s.length();
memset(val,0,sizeof val);
memset(dp,0,sizeof dp);
for(int i=0;i<n;i++)
for(int j=1;i+j-1<n;j++){
cal(s.substr(i,j),i,i+j-1);
}
memset(dp,0x3f,sizeof dp);
for(int i=0;i<n;i++)
dp[i][1]=val[0][i];
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
for(int x=2;x<=k;x++){
dp[i][x]=min(dp[i][x],dp[j][x-1]+val[j+1][i]);
//printf("i=%d,j=%d,x=%d,dp=%d,val=%d\n",i,j,x,dp[j][x-1],val[j+1][i]);
}
return dp[n-1][k];
}
};