【蓝桥杯冲刺 day7】 题目全解析 --- 附上LeetCode周赛 银联-03. 理财产品

写在前面

大噶好哇,我是秋刀鱼,因为要复习考研的内容所以现在码代码的时间越来越少了,不过这也无法磨灭了一个即将秃顶的程序员热爱代码的心,去NND考研我要开摆!
不知不觉呢蓝桥杯每日打卡的第七天了,因为今天LeetCode题目还有打卡题目还算简单所有有时间来写写题解,希望看完后能对你在做题上能有所帮助。ღ( ´・ᴗ・` )比心

在这里插入图片描述

相乘

题目链接
image-20220314182511016

解题思路:

枚举区间 [ 1 , 1000000007 ] [1,1000000007] [1,1000000007]的所有数值,如果该值乘2021后取余1000000007 后值为 999999999 ,输出并返回。

public class Main {
    public static void main(String[] args) {
        long num = 0;
        for (long i = 1; i <= 1000000007; i++) {
            if (i * 2021 % 1000000007 == 999999999) {
                num = i;
                System.out.println(num);
                return;
            }
        }
    }
}

空间

题目链接
image-20220314182734359

解题思路:

这里需要知道计算机的基础知识: 1 M B = 1024 K B = 1024 ∗ 1024 B = 1024 ∗ 1024 ∗ 8 b i t 1MB = 1024 KB = 1024*1024B = 1024*1024*8 bit 1MB=1024KB=10241024B=102410248bit

计算机中常说的"位"也就是bit单位,所以算出 256 M B 256MB 256MB共有多少个位。因为不需要考虑其他的因素,所以直接将位数目除以32就是存储32位二进制整数的数量。

注意溢出,要使用long long 型数据存储结果。

#include <iostream>
using namespace std;
int main()
{
  cout<<256L*1024*1024*8/32;
  return 0;
}

发现环

题目链接
image-20220314183123567

解题思路:

一道典型的双向拓扑排序问题,如果不了解拓扑排序的小伙伴一定要自行学习后再做这道题。

  • 定义inNode[]存储每一个节点的入度,因为网络路径是双向路径,因此在添加一条新的路径时,路径两端的点入度都需要加上一
  • 定义arr[][]存储路径,arr[from][0]=to || arr[from][1] =to都能表示from节点与to节点相连接
    • 为什么需要的是一个二维数组呢?因为路径中增加了一条线路,导致必定会有一个结点与两个结点相连接,因此使用一个二维数组来存储

对于拓扑排序,定义一个队列存储遍历的元素,首先将入度为1的边缘结点放入队列中,并使其入度减小1,随后在队列中取出结点的索引,遍历该结点相连的结点,使其入度减小1,如果入度为1则将该相连的结点加入队列中,最终入度>1的结点,也就是没有被存入队列的结点,就是环中的结点。

// 1:无需package
// 2: 类名必须Main, 不可修改

import java.io.*;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    public static void main(String[] args) throws IOException {
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out));
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(reader);


        in.nextToken();
        int n = (int) in.nval;
        int[][] arr = new int[n + 1][2];
        int[] inNode = new int[n + 1];
        for (int i = 1; i <= n; ++i) {
            in.nextToken();
            int to = (int) in.nval;
            in.nextToken();
            int from = (int) in.nval;
            // 入度+1
            inNode[to]++;
            inNode[from]++;
            // 保存方向
            if (arr[from][0] != 0) {
                arr[from][1]=to;
            }else{
                arr[from][0] = to;
            }
            if (arr[to][0] != 0) {
                arr[to][1]=from;
            }else{
                arr[to][0] = from;
            }
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 1; i <= n; ++i) {
            // 将入度为1的元素放入队列
            if (inNode[i] == 1) {
                queue.add(i);
                inNode[i]--;
            }
        }
        while (!queue.isEmpty()) {
            int cur = queue.poll();
            // 遍历其相邻结点
            for (int t = 0; t <= 1; ++t) {
                int nx = arr[cur][t];
                if (nx != 0) {
                    arr[cur][t] = 0;
                    inNode[nx]--;
                    if (inNode[nx] == 1) {
                        queue.add(nx);
                    }
                }
            }
        }
        for (int i = 1; i <= n; ++i) {
            if (inNode[i] > 1) {
                writer.print(i + " ");
            }
        }
        writer.flush();
    }
}

LeetCode 周赛 银联-03. 理财产品

题目链接
image-20220314190133597

好久没有看leetCode周赛的题目了,昨天也是没有时间所以没能够参加,今天抽空看了看后两道题,看到第四题的通过率低的离谱直接选择放弃,看了看第三题还是有些把握,于是乎就有了第三题的题解。

其实这道题还算是一道简单的模拟题,读题是想到可以使用优先队列取回、放入的操作实现,但是一看limit的范围 [ 1 , 1 0 9 ] [1,10^9] [1,109]好家伙直接排除,想了想还是只有模拟的方法靠谱。

为了使投入的金额总和最大,那么根据贪心的思想每次参与的项目一定是投资金额最大的项目,因此我们可以将product中的数据进行一个排序,大的值放在后面,那么我们优先拿后面的元素一定是符合题意的。

但是只拿最后一个元素远远不够,因为一个项目被投入后其资金会减小1,因此核心思想是:尽可能多的拿取最大值,那么最后一个元素一定是最大值时,会有一个临界值,该值就是当最后的元素值与次最大值相等时,此时我们可以选择拿取最后一个元素与次最大值。

那么为了记录排序后前一个元素与后一个元素的差值,我们定义一个差值数组gap来存储差值, g a p [ i ] = p r o d u c t [ i + 1 ] − p r o d u c t [ i ] gap[i]=product[i+1]-product[i] gap[i]=product[i+1]product[i],定义最后一个元素gap值为0。

拿数据 【2,4,5,6,8】来举例,那么那么gap数组的值为:【2,1,1,2,0】

开始时候,拿取最后元素值,直到与次最大值相同,也就是拿取【8,7】,此时数组为:【2 4 5 6 6】

因为最大值6有两个项目,那么这时我们再拿取就是【6,6】,此时数组为【2 4 5 5 5】

此时最大值5有三个项目,那么此时我们再拿取就是【5,5,5】,此时数组为【2 4 4 4 4】

此时最大值4有四个项目,那么此时我们再拿取就是 【4,4,4,4】【3,3,3,3】,数组为【2,2,2,2,2】

不知道你有没有发现规律,就是拿取一次后,下一次可拿取的数量每次都会增加1,也就是最大值是原来拿取最大值+1,且每次能够拿取到的最小值,就是次最大值+1, 能拿取的值【最大值,最大值-1,最大值-2,…,次最大值+1】

  • 定义idx指向次最大值的索引

  • 定义num为可以拿取的最大数量,也就是数组中最大值的数量

  • 定义gapNum最大值到次最大值的差值

那么此时编写cost函数用于消费limit,具体细节看注释。

class Solution {
    final long MOD = 1000000007;
    long ans = 0;
    long limit;
    public void cost(long num, long value,long gapNum) {
        // 可用拿下本轮所有值
        if (limit >= num * gapNum) {
            // 等差数列求和公式
            ans += num * ((long) (value + (value - gapNum + 1)) * gapNum / 2);
            limit -= num * gapNum;
        }
        // 无法拿下所有值
        else {
            // 能够投入的组数,例如【4,4,4,4】就是一组,【3,3,3,3】也是一组,
            long groupSize = limit / num;
            // 剩余无法投入完一组但是能投入剩下的部分,例如【2,2,2,2】中消费【2,2】
            long restSize = limit % num;
            // 剩余部分投入的值
            long restValue = value - groupSize;
            // 等差数列求和公式
            ans += num * ((value + (long) (value - groupSize + 1)) * groupSize / 2);
            ans += (long) restValue * restSize;
            limit=0;
        }
        ans %= MOD;
    }
    public int maxInvestment(int[] product, int limit) {
        long n = product.length;
        this.limit = limit;
        long[] gap = new long[(int) n];
        Arrays.sort(product);
        for (int i = 0; i < n - 1; ++i) {

            gap[i] = product[i+1] - product[i];
        }

        long idx = n - 2;
        while (idx >= 0 && this.limit > 0) {
            // 递减数
            long gapNum = gap[(int) idx];
            // 当前可用最大值
            long value = product[(int) (idx + 1)];
            // 个数
            long num = n - 1 - idx;
            cost( num, value, gapNum);
            ans %= MOD;
            --idx;
        }
        // 判断如果 limit > 0
        if (this.limit > 0) {
            // gapNum = product[0] ,因为最小值到0,所以与次最小值0的差为改值本身
            // num = n
            // value = procduct[0] 因为此时剩下的项目金额均相同,最大值是product[0]
            cost(n, product[0], product[0]);
        }
        
        return (int) ans;
    }
}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋刀鱼与猫_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值