leetcode第219场周赛总结
这次的周赛后两道题都是动态规划的,最后一题想复杂了 没做出来,其实也没有那么难
第一题
题目链接: 比赛中的配对次数.
解题思路:
看图说话型题目,没什么好写的
class Solution {
public int numberOfMatches(int n) {
if(n == 1) return 0;
int ans = 0;
while(n >= 2){
ans += n / 2;
n = (n + 1) / 2;
}
return ans;
}
}
第二题
十-二进制数的最少数目
题目链接: 十-二进制数的最少数目.
解题思路:
题目有点难懂,仔细看的话就觉得很简单了。十-二进制意思就是每一个位上都只能放1或者0,但是这个数还是十进制的。比如1001就是十-二进制,但他代表的是一千零1。
所以因为每一位都可以是1/0,我们最多用9个这样的十-二进制数就可以表示每个标准10进制数,于是就变成了求字符串中最大的数字,返回即可。
class Solution {
public int minPartitions(String n) {
int ans = 0;
for(char c: n.toCharArray()){
if(c - '0' > ans){
ans = c - '0';
}
}
return ans;
}
}
第三题
题目链接: 石子游戏 VII.
解题思路:
如果大家和我一样,是个身经百战的笨比,这道题应该不陌生了,很典型的动态规划题。这道题和石子游戏一几乎一样的解题思路,但这也是有个前提:
爱丽丝总是能赢,石子没有负数。
因为没有负数,鲍勃的策略就从缩小分差变成了拿到最大的得分(因为他怎么都赢不了,所以不用考虑让分给爱丽丝),如果有正负,那么情况就变了。
思路很正常,典型的区间从小到大进行状态转移,因为要求区间中的和,所以要获取前缀和的数组。
dp[i][j]代表区间i - j上可以得到的最小分差(其实就是此时某个人选择时可以得到的最大得分),因为我只能选两边的石子,所以当处于i - j这个区间时,得到的分差就等于选择i 得到的分差 和 选择j得到分差中的最大值,为什么是最大呢?因为两位的策略都是扩大自己的得分(爱丽丝是题目所述,鲍勃是迫不得已)
于是转移方程就出来了:
区间[i, j]的最小差 (最大得分)= max(选 i 的得分 - 剩下区间里下一个人的得分,选j的得分 - 剩下区间里下一个人的得分)
class Solution {
public int stoneGameVII(int[] stones) {
int[] preAdd = new int[stones.length];
preAdd[0] = stones[0];
for(int i = 1;i<stones.length;i++) {
preAdd[i] = preAdd[i - 1] + stones[i];
}
int[][] dp = new int[stones.length][stones.length];
for (int len = 0;len < stones.length;len ++){
for(int i = 0;i< stones.length - len;i++){
if(len == 0) dp[i][len + i] = 0;
else {
int choose_left = preAdd[len + i] - preAdd[i];//选择i能拿到的分数
int choose_right = i == 0 ?preAdd[i + len - 1] : preAdd[i + len - 1] - preAdd[i - 1]; //选择J能拿到的分数,要考率i是否为0的情况
dp[i][len + i] = Math.max(choose_left - dp[i + 1][len + i], choose_right- dp[i][i + len - 1]);//最后的分差等于选择i能拿到的分数减去[i+1,j]的分差 和 选择j能拿到的分数减去[i, j- 1]的分差 中的最大值
}
}
}
return dp[0][stones.length -1 ];
}
}
第四题
题目链接: 堆叠长方体的最大高度.
解题思路:
这道题的解题点是长,宽,高三个维度都要大于才可以堆叠。于是我们就想到了排序,因为长方体可以旋转,所以三个维度可以任意转换。所以首先我们对每个长方体三个维度按照大小排序,排完序之后再按照每个长方体最大的那个边对所有长方体排序。
这样的话就意味着排完序之后,只能把排在前面的长方体堆在排在后面的长方体上,后面长方体的不能堆在前面的长方体上面(除非完全一样)。
然后我们就得到了这样一个数组[[a1,a2,a3],[b1,b2,b3],[c1,c2,c3]…]
其中 a3>=a2>=a1, b3>=b2>=b1, c3>=c2>=c1, c3>=b3>=a3
然后这个问题就变成了标准的动态规划问题,我们从小的长方体开始遍历,找出以这个长方体为底座的最大高度。
dp[j]代表以这个长方体为底座的最大高度。
每往后历遍一个长方体j,就对前面已经历遍的长方体i进行判断,如果可以堆在长方体j上,则更新dp[j] = max(dp[j], dp[i] + H[j])。最后输出dp数组中的最大值即可。
最后的问题,每个长方体的高度用哪个边呢?答案很简单,用最长的那个,因为堆叠的条件是长,宽,高三个维度都要大于才可以堆叠,既然都大于,为了最大化高度,肯定是最高的立起来比较好嘛。
比赛的时候我还在纠结dp是不是选和不选的dp,搞的没写出来
class Solution {
public int maxHeight(int[][] cuboids) {
for (int[] arr : cuboids) {
Arrays.sort(arr);
}
Arrays.sort(cuboids, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[2] == o2[2]) {
if (o1[1] == o2[1]) {
return o1[0] - o2[0];
} else {
return o1[1] - o2[1];
}
} else {
return o1[2] - o2[2];
}
}
});
int ans = 0;
int[] dp = new int[cuboids.length];
for (int i = 0;i<cuboids.length;i++){
dp[i] = cuboids[i][2];
for(int j = 0;j<i;j++){
if (compare(cuboids[i],cuboids[j])){
dp[i] = Math.max(dp[i], dp[j] + cuboids[i][2]);
}
}
ans = Math.max(ans, dp[i]);
}
return ans;
}
public boolean compare(int[] arr1, int[] arr2){
if(arr1[2] >= arr2[2]){
if(arr1[1] >= arr2[1]){
return arr1[0] >= arr2[0];
}else {
return false;
}
}else {
return false;
}
}
}
总结
这次的最后一题是属于正常范畴的,差一点就写出来了,有点可惜,时间结束之后才想明白。感觉除了一些很变态的图论题或者奇奇怪怪的dp题,其他的题像我这种一般人也可以写一下。
文中的题目描述和图片均来自于leetcode
链接: link.