因为近期开始春招了,很难和之前一样有非常充分的时间每周都跟着周赛刷题了。但是总结做过的算法题,这个事情还是给我在春招实习的过程中带来了很大的帮助。我也会继续坚持。
文章目录
1814. 统计一个数组中好对子的数目(寻找合适的哈希表使用)
重点说一下思路, 每次的变化可以考虑为rev(x) = x+k,其中k是一个差值,表示了反转以后的数值和当前数值的差距。 因此x+rev(y) = x + y+ k1; y+rev(x) = x+y+k2 ; 因此我们需要找到k1 = k2的数量。
class Solution {
public int countNicePairs(int[] nums) {
// 每次的变化可以考虑为rev(x) = x+k
// 因此x+rev(y) = x + y+ k1; y+rev(x) = x+y+k2 ; 因此我们需要找到k1 = k2的数量
HashMap<Integer, Integer> map = new HashMap<>();
long ans = 0;
int mod = 1000000007;
for(int num:nums){
int k = num-rev(num);
int cur = map.getOrDefault(k, 0);
ans += cur;
ans %= mod;
map.put(k, cur+1);
}
return (int)ans;
}
private int rev(int num){
int ans = 0;
while(num>0){
ans = ans*10+num%10;
num /= 10;
}
//System.out.println(ans);
return ans;
}
}
1815. 得到新鲜甜甜圈的最多组数(模拟退火,dfs)
首先当我看到这个题目的时候,第一反应是使用状态压缩DP。因为数据范围不是非常的大,大概是 2 30 2^{30} 230的范围。但是因为我们还需要额外枚举最后一题添加了哪个数字,因此最终的复杂度应该是 30 ∗ 2 30 30 * 2^{30} 30∗230的时间复杂度。这是超了的。
首先说一下比较容易理解的dfs的方法,我们首先整理每个顾客希望买的个数,对batchsize取模。这样所有的数据都映射到了[0, batchSize-1]的范围里。然后我们排除掉不用额外处理余数为0的,剩下的类型我们进行dfs搜索。dfs的参数包括,当前剩余未安排的顾客的数组nums,和当前剩余的面包个数res。这里使用了记忆化搜索。
然后再来一个新学习的方法,模拟退火方法。这个方法更像是一种随机化的搜索,找到一种最佳的排列方式,每次随机的交换两个位置,然后计算成本。如果成本降低了,我们就采用这个方法。如果没有降低,我们有一定的概率的接受这个方法。之所以有一定给的可能接受一个不如目前的方法,是为了防止陷入局部最优解,相当于我们可以直接跳出去。这样我们最后可以得到全局最优解。
需要特别在意的点在于:
- 首先我们很容易的定义一个变化量
Delta = y-x
也就是交换以后的只减去交换之前的值。其中x, y分别是交换前后计算的成本,这个成本是与我们题目相关的结果。但是一定要满足最优的应该是最小的原则。也就是我们如果想求最小值,可以直接返回。如果是最大值,需要变成负数再返回。 - 成本函数: e x p ( − d e t l a / t ) > M a t h . r a n d o m ( ) exp(-detla/t)>Math.random() exp(−detla/t)>Math.random()这里首先给出了一个概率是在[0,1]之间的。然后我们需要注意,这里的
delta
一定是小于零的。因此整个函数是在x的负半轴,值域在(0,1)的一个指数函数。并且当参数t
越小的时候,越接近x的负无穷,概率越低,也就越不容易被接受。因此我们整个的过程是一个降温的过程,最开始的时候,t
是 1 0 6 10^6 106是一个比较高温的过程,这个阶段很容易被接受进行试探,容易跳出局部最优解;随着过程的慢慢降温,t
逐渐减小到 1 0 − 6 10^{-6} 10−6,这个时候很难接受一个不如当前值的试探了。 - 整个退火过程,会被重复多次,取一个最大值。
核心函数:swap函数实现交换数组的数字。cost函数计算当前数组的排列方式的成本,返回最后的成本。这个函数的内部可以执行比较逻辑,利用全局变量保存最值。simulate_anneal函数,模拟进行退火过程,需要合理设置初始温度t
和退火速度d
这两个参数。最后就是一个主函数,负责多次重复的模拟退火。
class Solution {
//模拟退火算法 -leetcode 5707. 得到新鲜甜甜圈的最多组数
int ans;
int m;
Random random;
public int cost(int[] w) {
//计算当前排列的代价
int n = w.length;
int res = 0;
for (int i = 0, s = 0; i < n; i++) {
if (s == 0) res++;
s = (s + w[i]) % m;
}
ans = Math.max(ans, res);
return -res;//内能和收益是负相关 收益大内能小
}
public void swap(int[] w, int i, int j) {
int t = w[i];
w[i] = w[j];
w[j] = t;
}
public void simulate_anneal(int[] w) {
double d = 0.97;//降温系数
int n = w.length;
// 循环不断进行降温
for (double t = 1e6; t > 1e-6; t = t * d) {
// 模拟退火的过程 t 为 温度 慢慢下降的过程, 越到后面整个状态要更趋于稳定. 即当 t 越低 保留操作概率下降
int a = random.nextInt(n);
int b = random.nextInt(n);
//交换两个位置
if (a == b) b = (b + 1) % n;
// 计算最初状态的成本
int x = cost(w);
swap(w, a, b);
// 计算交换以后成本
int y = cost(w);
// 计算差值
int delta= y-x;
// 如果新的内能小,直接接受
if(delta<0) continue; //新的解的内能更小
// 否则有一定的概率接受
if((Math.exp(-delta/t)>Math.random())) continue;//接受
//否则,不接受当前新的解,换回去
swap(w,a,b);
}
}
public int maxHappyGroups(int batchSize, int[] groups) {
int step = 50;
ans = 0;
m = batchSize;
random = new Random();
for (int i = 0; i < step; i++) {
simulate_anneal(groups);
}
return ans;
}
}
================================DFS方法=============================
public int maxHappyGroups(int batchSize, int[] groups) {
int step = 50;
ans = 0;
m = batchSize;
random = new Random();
for (int i = 0; i < step; i++) {
simulate_anneal(groups);
}
return ans;
}
private HashMap<String,Integer> map =