1. 可怜的小猪
有1000只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在15分钟内死去。
问题来了,如果需要你在一小时内,弄清楚哪只水桶含有毒药,你最少需要多少只猪?
回答这个问题,并为下列的进阶问题编写一个通用算法。
进阶:
假设有 n 只水桶,猪饮水中毒后会在 m 分钟内死亡,你需要多少猪(x)就能在 p 分钟内找出“有毒”水桶?n只水桶里有且仅有一只有毒的桶。
分析:
需要在一小时内测试出哪桶水有毒,而每头猪喝下毒药会在15分钟内死去,因此猪就有了五个时间状态,0min,15min,30min,45min,60min.
当测试用例buckets是1时,这桶水就是有毒的水,因此就是需要0头猪;
1头猪1个小时内可以测5桶水,每个状态下喝下一桶,若在某个时间点死去,则就是这桶水有毒。
先看一下2头猪可以测多少桶水呢?将每桶水按二维来编号,由于猪有5个状态,每行每列放5桶水 :
上述的两个箭头分别代表两个猪测试水的方向。
猪1和猪2在0min分别喝第0行和第0列的水,如果两头猪都没事,接着按行按列往下喝,如果某个时间点猪1或猪2死去了,说明猪1或猪2喝的某行某列有水有毒
猪1死去,猪2没死,猪1测出了哪行水有毒, 猪2需要在剩下的时间在每个时间点喝下此行剩下的水 ,直到死去,测出哪桶水有毒。猪2死去,猪1没死同理。
若猪1和猪2同时死去,那就是他们喝的行列交叉处的水有毒。
分析完2头猪可以测25桶水,我们可以得知每个猪可以占一个维度来测试水,即3头猪可以测5^3=125桶水,4头猪可以测5^4=625桶水,5头猪可以测5^5=3125>1000桶水,因此需要5头猪就可以测出来。
进阶:
先算出有几个时间状态: minutesCount = p/m +1;
x头猪各占一个维度: 当 minutesCount^x<n && minutesCount^x>=n时,x就是我们需要的小猪数.
代码:
public class Test_0131 {
public static void main(String[] args) {
Solution So = new Solution();
int buckets = 1000;
int minutesToDie = 15;
int minutesToTest = 60;
System.out.println(So.poorPigs(buckets, minutesToDie, minutesToTest));
}
}
class Solution {
public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
int minutesCount = minutesToTest/minutesToDie +1;
int poorPigs = 0;
int maxBuckets = 1;
while(maxBuckets < buckets){
poorPigs++;
maxBuckets *= minutesCount;
}
return poorPigs;
}
}
2. 最小移动次数使数组元素相等
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动可以使 n - 1 个元素增加 1。
示例:
输入:
[1,2,3]
输出:
3
解释:
只需要3次移动(注意每次移动会增加两个元素的值):
[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
分析:
正向思考:计算数组最大元素与最小元素的差,给小于最大元素的数组元素都加上这个数,判断数组的元素是否已全部相等,如果不相等,接着执行上述操作,直到全部相等,为了得到最大元素和最小元素,可以使用数组排序。
逆向思考:每次移动让除去最大元素的剩余n-1个数加1,相当于每次移动让选定的数减1,所以最少移动次数其实就是所有元素减去最小元素的和。
代码:
class Solution {
public int minMoves1(int[] nums) {
int count = 0;
while(compare(nums) == false){
Arrays.sort(nums);//数组排序
int n =nums[nums.length-1]-nums[0];
for(int i = 0; i<nums.length-1; i++){
nums[i] += n;
}
count += n;
}
return count;
}
private boolean compare(int[] nums) {
int c = nums[0];
for(int i=1; i<nums.length; i++){
if(nums[i] !=c){
return false;
}
}
return true;
}
public int minMoves2(int[] nums) {
int sum = nums[0];
int min = nums[0];
for(int i=1; i<nums.length; i++){
sum += nums[i];
if(nums[i] <min)
min = nums[i];
}
return sum -nums.length*min;
}
}