T2-达到目标的最小操作次数
-
intution:要充分利用加倍,必须在数尽量大时加倍。
- 如果target是奇数,则最后一步一定是递增1.
- 如果target是偶数,则最后一步是什么?
贪心选择
- 一定存在一种最优解,在target是偶数时,最后一步的操作是加倍。
- 证明:如果所有的最优解最后一步都不是加倍。任取一个最优解,最后一步加倍在小于target/2的数上进行,设为kkk。即将kkk加倍到2k2k2k后再将其递增到target。从k开始,这个最优解的操作次数为:将k加倍到2k,从2k递增到target,总操作次数target - 2k + 1;而如果直接将k先递增到target/2target/2target/2,再将其翻倍,总操作次数target/2−k+1target / 2 - k + 1target/2−k+1,显然得到了一个更有的解,矛盾。
最优子结构(显然)
递归算法
class Solution {
public int minMoves(int target, int maxDoubles) {
if(target == 1)
return 0;
if(maxDoubles == 0)
return target - 1;
if(target % 2 == 0)
return minMoves(target / 2,maxDoubles - 1) + 1;
else return minMoves((target - 1)/2,maxDoubles - 1) + 2;
}
}
T4-同时运行 N 台电脑的最长时间
二分
- 前面周赛遇到过类似的题。确定可能的解空间后,使用二分查找找到最优解。
- 但问题的关键在于:给定一个时间,判断电脑是否能同时运行如此之长?关键在于形式化。
- 使用矩阵处理(想不到)。给定待判时间kkk,电脑数目nnn,设矩阵A∈RknA \in R^{kn}A∈Rkn,AijA_{ij}Aij表示第i分钟电脑j使用电池AijA_{ij}Aij,Aij∈[0,m−1]A_{ij} \in [0,m-1]Aij∈[0,m−1]为电池编号,A是合法矩阵的充分必要条件是(也就是k是一个可行解)
- 矩阵A的每一行元素互不相同
- ∀x∈[0,m−1]\forall x \in [0,m-1]∀x∈[0,m−1],其在A中出现的次数小于等于battery[x]battery[x]battery[x]
设:occ(x)=min{k,battery[x]}occ(x) =min\{k,battery[x]\}occ(x)=min{k,battery[x]}若A合法,则x在矩阵A中出现的次数小于等于occ(x)
- 命题:AAA是合法矩阵的充要条件是∑x=0m−1occ(x)≥k⋅n\sum_{x = 0}^{m-1} occ(x) \geq k \cdot nx=0∑m−1occ(x)≥k⋅n
- 如果A合法, ∑x∈Aocc(x)≥k⋅n\sum_{x \in A} occ(x) \geq k \cdot n∑x∈Aocc(x)≥k⋅n,而∑x=0m−1occ(x)≥∑x∈Aocc(x)≥k⋅n\sum_{x = 0}^{m-1} occ(x) \geq \sum_{x \in A} occ(x) \geq k \cdot n∑x=0m−1occ(x)≥∑x∈Aocc(x)≥k⋅n,即证。
- 反之,如果∑x=0m−1occ(x)≥k⋅n\sum_{x = 0}^{m-1} occ(x) \geq k \cdot n∑x=0m−1occ(x)≥k⋅n, 对于每个x,将其填入A的不同行中occ(x)次。对于每个x重复直到将A填满。由occ(x)的定义,A是合法的。
- 于是对于给定的k,只需判断∑x=0m−1occ(x)≥k⋅n\sum_{x = 0}^{m-1} occ(x) \geq k \cdot n∑x=0m−1occ(x)≥k⋅n是否成立,即可确定k是否是可行解。确定Binary Search的上界为⌊∑ibattery[i]/n⌋\lfloor \sum_i battery[i] / n \rfloor⌊∑ibattery[i]/n⌋,下界为0,搜索最后一个满足条件的k值。采用先前总结的二分查找框架实现:
class Solution {
public long maxRunTime(int n, int[] batteries) {
long high = 0;
for(int i : batteries)
high += (long)i;
high/=n;
long low = 1;
high++;
while(low < high)
{
long mid = low + ((high - low) >> 1);
long check = 0;
for(int i : batteries)
check += Math.min((long)i,mid);
if(check >= mid * n)
low = mid + 1;
else high = mid;
}
return low - 1;
}
}