876. 链表的中间结点
给定一个带有头结点 h e a d head head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1 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 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
提示:
1. 给定链表的结点数介于 1 和 100 之间。
思路:求出链表的长度 l e n len len;从链表头开始遍历 l e n / 2 len/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] piles[i] 。
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 t r u e true true ,当李赢得比赛时返回 f a l s e false 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] dp[i][i+len]表示从 i i i开始到 i + l e n i + len i+len,亚历克斯最多能赢李多少分
- 考虑一个石子,那么亚历克斯得分即为 p i l e s [ i ] piles[i] piles[i], i i i为当前考虑的石子,那么有 d p [ i ] [ i ] = p i l e s [ i ] dp[i][i] = piles[i] dp[i][i]=piles[i]
- 考虑从 i i i开始后的 l e n len len个石子,如果亚历克斯选择了第 i i i个石子,那么从 i + 1 i + 1 i+1到 i + l e n i + len i+len个石子,有 d p [ i + 1 ] [ i + l e n ] dp[i + 1][i + len] 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] dp[i][i+len]=piles[i]−dp[i+1][i+len];同理如果亚历克斯选择了第 i + l e n i+len 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] 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]) 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 A 或 B B B 整除,那么它是神奇的。
返回第 N N N 个神奇数字。由于答案可能非常大,返回它模 1 0 9 + 7 10^9 + 7 109+7 的结果。
示例 1 1 1:
输入:N = 1, A = 2, B = 3
输出:2
示例 2 2 2:
输入:N = 4, A = 2, B = 3
输出:6
示例 3 3 3:
输入:N = 5, A = 2, B = 4
输出:10
示例 4 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 TLE
参考大神做法:二分法寻找值。 l e f t left left为该序列的最小值 ( m i n ( A , B ) ) (min(A, B)) (min(A,B)), r i g h t right right即为序列可能达到的最大值 ( m a x ( A , B ) ∗ 1 l l ∗ N ) (max(A, B) * 1ll * N) (max(A,B)∗1ll∗N), m i d = ( l e f t + r i g h t ) > > 1 mid = (left + right) >> 1 mid=(left+right)>>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 len=mid/A+mid/B−mid/lcm( l c m = A ∗ B ∗ 1 l l lcm = A * B * 1ll lcm=A∗B∗1ll / __ g c d ( A , B ) gcd(A, B) gcd(A,B),即为最小公倍数), m i d mid mid / A A A表示 A A A序列的长度, m i d / B mid / B mid/B表示 B B B序列的长度, m i d / l c m mid / lcm mid/lcm表示两个序列公共元素;
- (1)如果 l e n < N len < N len<N,那么 l e f t = m i d + 1 left = mid + 1 left=mid+1
- (2)如果 l e n = = N len == N len==N,那么判断 m i d mid % A == 0 || mid % B == 0 mid是否成立,如果成立则说明最终元素等于 m i d mid mid,退出循环
- (3)如果 l e n > N len > N len>N, r i g h t = m i d − 1 right = mid - 1 right=mid−1
- (4)更新 m i d mid mid的值, m i d = ( l e f t + r i g h t ) > > 1 mid = (left + right) >> 1 mid=(left+right)>>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;
}
};