【周赛总结】1812-1840(模拟退火,约瑟夫问题,数据流问题,timeline,左右遍历,三维DP)

因为近期开始春招了,很难和之前一样有非常充分的时间每周都跟着周赛刷题了。但是总结做过的算法题,这个事情还是给我在春招实习的过程中带来了很大的帮助。我也会继续坚持。

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} 30230的时间复杂度。这是超了的。

首先说一下比较容易理解的dfs的方法,我们首先整理每个顾客希望买的个数,对batchsize取模。这样所有的数据都映射到了[0, batchSize-1]的范围里。然后我们排除掉不用额外处理余数为0的,剩下的类型我们进行dfs搜索。dfs的参数包括,当前剩余未安排的顾客的数组nums,和当前剩余的面包个数res。这里使用了记忆化搜索。

然后再来一个新学习的方法,模拟退火方法。这个方法更像是一种随机化的搜索,找到一种最佳的排列方式,每次随机的交换两个位置,然后计算成本。如果成本降低了,我们就采用这个方法。如果没有降低,我们有一定的概率的接受这个方法。之所以有一定给的可能接受一个不如目前的方法,是为了防止陷入局部最优解,相当于我们可以直接跳出去。这样我们最后可以得到全局最优解。

需要特别在意的点在于:

  1. 首先我们很容易的定义一个变化量Delta = y-x也就是交换以后的只减去交换之前的值。其中x, y分别是交换前后计算的成本,这个成本是与我们题目相关的结果。但是一定要满足最优的应该是最小的原则。也就是我们如果想求最小值,可以直接返回。如果是最大值,需要变成负数再返回。
  2. 成本函数: 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} 106,这个时候很难接受一个不如当前值的试探了。
  3. 整个退火过程,会被重复多次,取一个最大值。

核心函数: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 =
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值