LeetCode 周赛202

本文介绍了LeetCode周赛202中的四道题目,包括存在连续三个奇数的数组、使数组元素相等的最小操作数、两球之间的磁力和吃掉N个橘子的最少天数。通过分析题目的解题思路,重点探讨了动态规划和数组处理的方法,如等差数列求和公式和二分查找等策略。

好久没有打周赛了,今天在紧张的考前复(预)习中抽时间打一场就很刺激。

总体来说这套题还算可以,难度适中。

存在连续三个奇数的数组

题目大意就是给定一个数组,检测其中是否存在三个连续的奇数。

这个算是签到题啦,扫一遍记录一下就好:

class Solution {
    public boolean threeConsecutiveOdds(int[] arr) {
		int flag = 0;
		int n = arr.length;
		for(int i = 0;i < n;i++) {
			if(arr[i] % 2 == 1) {
				flag++;
			}else {
				flag = 0;
			}
			if(flag == 3) {
				return true;
			}
		}
		return false;
    }
}

使数组中所有元素相等的最小操作数

题目大意是给定一个数组,每次可以选择一对元素,给其中一个加一,另一个减一。问操作多少次后数组中元素都相同。

这道题也不难,注意到每次是给一个加一另一个减一,数组总和不变。最后结果一定是平均值。

如果数组是随机给定的,那么扫一遍统计每个元素到平均值差的绝对值,最后将加和除以2就是答案。

但是!!

这个题目中数组中的元素存在规律。

ai=2i−1(1≤i≤n) a_i = 2i - 1 \\ (1 ≤i≤n) ai=2i1(1in)
也就是说,这个序列为:
1,3,5,7.... 1,3,5,7.... 1,3,5,7....
那这就好整了。可以按照元素个数,分奇偶进行讨论

对于奇数,有:

avg=a⌊n2⌋=nans=∑i=1⌊n2⌋(avg−ai) =∑i=1⌊n2⌋(n−2i+1) avg = a_{\left \lfloor \frac{n} {2}\right \rfloor } = n\\ ans = \sum^ {\left \lfloor \frac{n} {2}\right \rfloor} _ {i = 1} (avg - a_i)\\ \ =\sum^ {\left \lfloor \frac{n} {2}\right \rfloor} _ {i = 1} (n - 2i+1) avg=a2n=nans=i=12n(avgai) =i=12n(n2i+1)
利用等差数列求和公式,有:
ans=(⌊n2⌋+1)∗⌊n2⌋ ans = (\left \lfloor \frac{n} {2}\right \rfloor+ 1) *\left \lfloor \frac{n} {2}\right \rfloor ans=(2n+1)2n

对于偶数,有:

avg=an2+an2+12=n−2ans=∑i=1n2(avg−ai) =∑i=1n2(n−2i−1) avg = \frac{a_{\frac{n} {2}} + a_{\frac{n} {2} + 1}} {2} = n - 2\\ ans= \sum^ {\frac{n} {2}} _ {i = 1} (avg - a_i)\\ \ =\sum^ {\frac{n} {2} }_ {i = 1} (n - 2i-1) avg=2a2n+a2n+1=n2ans=i=12n(avgai) =i=12n(n2i1)
利用等差数列求和公式,有:
ans=n2∗n2 ans = \frac{n} {2} * \frac{n} {2}\\ ans=2n2n
ans=n24 ans = \frac{n^2} {4} ans=4n2

public class Solution2 {
	int minOperations(int n) {
        if(n % 2 == 1) {
        	return (1 + n / 2) * (n / 2);
        }else {
        	return n * n / 4;
        }
    }
}

两球之间的磁力

题目大意是给定n个篮子和m个小球(n >=m),每个篮子都有一个一维坐标,两个小球的磁力值为|pi - pj|,p为小球所在篮子的位置。问最小磁力值最大是多少?

这个题,上来看到求最小值最大,那大概率跑不掉二分答案。

我们可以直接二分这个最小磁力值,然后每次遍历放置,检测是否能够放得下。

在二分之前,记得给所有篮子排个序

复杂度:
O(nlog(max(pi))) O(nlog(max(p_i))) O(nlog(max(pi)))

class Solution {
   private boolean check(int k,int[] p,int m) {
		int n = p.length;
		int flag = p[0];
		m--;
		for(int i = 1;i < n;i++) {
			if(p[i] - flag >= k) {
				m--;
				flag = p[i];
			}
			if(m == 0) {
				return true;
			}
		}
		return false;
	}
	public int maxDistance(int[] position, int m) {
		Arrays.sort(position);
		int n = position.length;
		int l = 0,r = position[n - 1];
		while(l < r) {
			int mid = (l + r) / 2 + 1;
			if(check(mid,position,m)) {
				l = mid;
			}else {
				r = mid - 1;
			}
		}
		return l;
    }

}

吃掉 N 个橘子的最少天数

题目大意是,给定n个橘子,每天可以选择三种吃法中的一种:

  • 吃掉一个橘子
  • 如果橘子数量是2的倍数,吃掉一半橘子
  • 如果橘子数量是3的倍数,吃掉三分之二橘子

问最少多少天可以将橘子吃完

将题目翻译一下,可以得到:

给定一个数字n,每次选择三种操作中的一种:

  • 让该数字减一
  • 如果该数字是2的倍数,让它除以二
  • 如果该数字是3的倍数,让它除以三

问最少多少次操作可以将数字减少到0

这么看的话,这是一道数学上的问题。如果这个数字的范围较小,在线性的时间内可以解决,我们可以选择采用动态规划来解决这个问题。

但是题目中这个数字的规模很大,有2*109 ,线性时间解决不了也存不下。

那么搜索也是一个好办法。不过搜索的复杂度非常高,我们需要对它进行一系列的优化:

  1. 我们要使得数字下降的尽可能快,因此我们先考虑除以三和除以二,最后在考虑减一。
  2. 对搜索过程进行剪枝优化,结束操作数量多于已知最小答案的过程

仅进行前两部优化的算法还不足以在规定时间内完成任务,因此我们还需要继续进行优化。

  1. 进行记忆化处理,考虑到在递归过程中,许多数字会经常出现,进行记忆化可以减少重复分解的次数。使用一个map记录每一个出现过的数字和分解到它时的最少步骤。如果一次分解到某数字的操作数不少于曾经出现过的最少操作数,那么这次的操作将一定不会对答案产生贡献,直接剪枝掉就好。否则更新map,继续搜索。
class Solution {
   static int ans = 1 << 30;
	static HashMap<Integer,Integer> map = new HashMap();
	private void dfs(int n,int k) {
		if(k >= ans) {
			return;
		}
		if(map.containsKey(n)) {
			if(map.get(n) <= k) {
				return;
			}
		}
		map.put(n, k);
		if(n == 0) {
			ans = k;
			return;
		}
		if(n % 3 == 0) {
			dfs(n / 3,k + 1);
		}
		if(n % 2 == 0) {
			dfs(n / 2,k + 1);
		}
		dfs(n - 1,k + 1);
	}
	public int minDays(int n) {
		map.clear();
        ans = 1 << 30;
		dfs(n,0);
		return ans;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值