【Leetcode】第95场周赛(前三题)

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/90237266
876. 链表的中间结点

给定一个带有头结点 h e a d head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1 1

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3(测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2 2

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 34,我们返回第二个结点。

提示:

1. 给定链表的结点数介于 1100 之间。

思路:求出链表的长度 l e n len ;从链表头开始遍历 l e n / 2 len/2 次到达中间节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        int len = length(head);
        for (int i = 1; i <= len/2; ++i) {
            head = head->next;
        }
        return head;
    }
    int length(ListNode* head) {
        int cnt = 0;
        while (head) {
            cnt++;
            head = head->next;
        }
        return cnt;
    }
};
877. 石子游戏

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 p i l e s [ i ] piles[i]

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 t r u e true ,当李赢得比赛时返回 f a l s e false

示例:

输入:[5,3,4,5]
输出:true
解释:
亚历克斯先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [3,4,5] 。
如果李拿走前 3 颗,那么剩下的是 [4,5],亚历克斯拿走后 5 颗赢得 10 分。
如果李拿走后 5 颗,那么剩下的是 [3,4],亚历克斯拿走后 4 颗赢得 9 分。
这表明,取前 5 颗石子对亚历克斯来说是一个胜利的举动,所以我们返回 true 。
 

提示:

1. 2 <= piles.length <= 500
2. piles.length 是偶数。
3. 1 <= piles[i] <= 500
4. sum(piles) 是奇数。

思路:动态规划。

  • d p [ i ] [ i + l e n ] dp[i][i + len] 表示从 i i 开始到 i + l e n i + len ,亚历克斯最多能赢李多少分
  • 考虑一个石子,那么亚历克斯得分即为 p i l e s [ i ] piles[i] i i 为当前考虑的石子,那么有 d p [ i ] [ i ] = p i l e s [ i ] dp[i][i] = piles[i]
  • 考虑从 i i 开始后的 l e n len 个石子,如果亚历克斯选择了第 i i 个石子,那么从 i + 1 i + 1 i + l e n i + len 个石子,有 d p [ i + 1 ] [ i + l e n ] dp[i + 1][i + len] ,即为李的最佳选择(李能赢亚历克斯的最大分数),此时亚历克斯最多能赢李的分数即为 d p [ i ] [ i + l e n ] = p i l e s [ i ] d p [ i + 1 ] [ i + l e n ] dp[i][i + len] = piles[i] - dp[i + 1][i + len] ;同理如果亚历克斯选择了第 i + l e n i+len 个石子,即有 d p [ i ] [ i + l e n ] = p i l e s [ i + l e n ] d p [ i ] [ i + l e n 1 ] dp[i][i + len] = piles[i + len] - dp[i][i + len - 1]
  • 综合上述两种情况,可得出动态转移方程为: d p [ i ] [ i + l e n ] = m a x ( p i l e s [ i ] d p [ i + 1 ] [ i + l e n ] , p i l e s [ i + l e n ] d p [ i ] [ i + l e n 1 ] ) dp[i][i + len] = max(piles[i] - dp[i + 1][i + len], piles[i + len] - dp[i][i + len - 1])
class Solution {
public:
    int dp[505][505];  //表示亚历克斯最多可以赢李的分数
    bool stoneGame(vector<int>& piles) {
        memset(dp, 0, sizeof(dp));
        int n = piles.size();
        for (int i = 0; i < n; ++i) {
            dp[i][i] = piles[i];
        }
        for (int len = 1; len < n; ++len) {
            for (int i = 0; i < n - len; ++i) {
                dp[i][i + len] = max(piles[i] - dp[i + 1][i + len], piles[i + len] - dp[i][i + len - 1]);
            }
        }
        return dp[0][n - 1] > 0;
    }
};
878. 第 N 个神奇数字

如果正整数可以被 A A B B 整除,那么它是神奇的。

返回第 N N 个神奇数字。由于答案可能非常大,返回它模 1 0 9 + 7 10^9 + 7 的结果。

示例 1 1

输入:N = 1, A = 2, B = 3
输出:2

示例 2 2

输入:N = 4, A = 2, B = 3
输出:6

示例 3 3

输入:N = 5, A = 2, B = 4
输出:10

示例 4 4

输入:N = 3, A = 6, B = 4
输出:8
 

提示:

1. 1 <= N <= 10^9
2. 2 <= A <= 40000
3. 2 <= B <= 40000

思路:刚开始使用栈暴力破解,然后 T L E TLE

参考大神做法:二分法寻找值。 l e f t left 为该序列的最小值 ( m i n ( A , B ) ) (min(A, B)) r i g h t right 即为序列可能达到的最大值 ( m a x ( A , B ) 1 l l N ) (max(A, B) * 1ll * N) m i d = ( l e f t + r i g h t ) &gt; &gt; 1 mid = (left + right) &gt;&gt; 1 ,那么整个序列的长度可以得出,即为 l e n = m i d / A + m i d / B m i d / l c m len = mid / A + mid / B - mid / lcm ( l c m = A B 1 l l lcm = A * B * 1ll / __ g c d ( A , B ) gcd(A, B) ,即为最小公倍数), m i d mid / A A 表示 A A 序列的长度, m i d / B mid / B 表示 B B 序列的长度, m i d / l c m mid / lcm 表示两个序列公共元素;

  • (1)如果 l e n &lt; N len &lt; N ,那么 l e f t = m i d + 1 left = mid + 1
  • (2)如果 l e n = = N len == N ,那么判断 m i d mid % A == 0 || mid % B == 0 是否成立,如果成立则说明最终元素等于 m i d mid ,退出循环
  • (3)如果 l e n &gt; N len &gt; N r i g h t = m i d 1 right = mid - 1
  • (4)更新 m i d mid 的值, m i d = ( l e f t + r i g h t ) &gt; &gt; 1 mid = (left + right) &gt;&gt; 1 ;
#define ll long long
class Solution {
public:
    const int mod = 1e9 + 7;
    int nthMagicalNumber(int N, int A, int B) {
        ll left = min(A, B), right = min(A, B) * 1ll * N;
        ll lcm = A * 1ll * B / __gcd(A, B);
        ll ans = -1;
        while (left <= right) {
            ll mid = (left + right) >> 1;
            ll cnt = mid / A + mid / B - mid / lcm;
            if (cnt < N) {
                left = mid + 1;
            }
            else {
                if (cnt == N && (mid % A == 0 || mid % B == 0)) {
                    ans = mid;
                    break;
                }
                right = mid - 1;
            }
        }
        return ans % mod;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/90237266