HNU-算法设计与分析-实验1

news2024/12/24 19:02:38

算法设计与分析
实验1

计科210X 甘晴void 202108010XXX
在这里插入图片描述

目录

文章目录

  • 算法设计与分析<br>实验1
    • (1)分治法查找最大最小值
      • 问题描述
      • 想法
      • 代码
      • 评测
      • 算法分析
    • (2)分治法实现合并排序
      • 问题描述
      • 想法
      • 代码
      • 评测
      • 算法分析
    • (3)实现题1-3 最多约数问题
      • 引言
      • 题目描述
      • 做法1(直接遍历求解+比较)
        • 代码
        • 评测
      • 做法2(使用约数定理计算)
        • 代码
        • 评测
      • 做法3(约数定理+DFS)
        • 代码
        • 评测
    • 实验感悟

(1)分治法查找最大最小值

问题描述

使用分治法查找给定数组a中的最大值与最小值

想法

可以看作二分查找的变形,区别在于不是有序的(我们是找值,也不需要有序),而且不是查找某个特定值(要找最小值和最大值)。

自上而下:将当前区域划分为前后两个区域,调用递归函数分别对这两个子区域进行划分,直到划分至不再可分为止(递归边界,只剩1个数),其最大值与最小值都是其本身。

自下而上:对于当前区域,将左子区域与右子区域分别比较最大值与最小值,获得当前区域的最小值与最大值,返回至父层。

代码

#include <iostream>
#include <math.h>
using namespace std;

void BinarySearch(int x[], int left, int right, int &maxi, int &mini)
{
    if (left == right)
    {
        maxi = x[left];
        mini = x[left];
        return;
    }
    else
    {
        int max1, min1, max2, min2;
        int mid = (left + right) / 2;
        // cout << left << " " << mid << " " << right << endl;
        BinarySearch(x, left, mid, max1, min1);
        BinarySearch(x, mid + 1, right, max2, min2);
        maxi = max(max1, max2);
        mini = min(min1, min2);
        return;
    }
}

int main()
{
    int n; // 数据量
    cin >> n;
    int x[n];
    for (int i = 0; i < n; i++)
        cin >> x[i];
    int maxi = x[0];
    int mini = x[0];
    BinarySearch(x, 0, n - 1, maxi, mini);
    cout << "max= " << maxi << endl;
    cout << "min= " << mini << endl;
}

评测

洛谷没有直接查找最大/最小值的原题,但是有类似的题目,可以测试一下。

找最小值(https://www.luogu.com.cn/problem/P5718)

在这里插入图片描述

找最大值(https://www.luogu.com.cn/problem/AT_chokudai_S001_a)

在这里插入图片描述

算法分析

近似于二分查找,时间复杂度O(n)。

空间复杂度O(n)。

(2)分治法实现合并排序

问题描述

使用分治法实现一个乱序数组a的排序

想法

二分排序属于是排序中的基础算法了。其主要思想在于使用分治算法。

自上而下:将当前区域划分为前后两个区域,调用递归函数分别对这两个子区域进行划分,直到划分至不再可分为止(递归边界,只剩1个数),其自然有序。

自下而上:对于当前区域,将左子区域与右子区域进行合并,此时左子区域与右子区域都是有序的,进行O(n)的合并后本层区域有序,返回父层。

代码

#include <iostream>
#include <math.h>
using namespace std;

void copy(int x[],int y[],int left,int right)
{
    for (int i=left; i<=right; i++) x[i]=y[i];
}

void Merge(int x[],int y[],int left,int mid,int right)
{
    // 合并x[left:mid] x[mid+1,right] 到 y[left:right]
    //cout<< "merge:"<<left<<" "<<mid<<" "<<right<<endl;
    int i=left,j=mid+1,k=left;
    while ((i<=mid) && (j<=right))
    {
        if (x[i]<=x[j]) y[k++]=x[i++];
        else y[k++]=x[j++];
        if (i>mid) //左边已经合并完了,剩下右边直接加入
            for (int t=j; t<=right; t++) y[k++]=x[t];
        else if (j>right) //右边已经合并完了,剩下左边直接加入
            for (int t=i; t<=mid; t++) y[k++]=x[t];
    }
    //cout<<"merge_result"<<endl;
    //for (int i=left;i<=right;i++)cout<<y[i]<<" ";
    //cout<<endl;
}

void MergeSort(int x[], int left, int right)
{
    if (left<right)
    {
        int mid=(left+right)/2;
        int y[right+1];
        //cout<<"left mid right "<<left<<" "<<mid<<" "<< right<<endl;
        //cout<<"mergesort"<<left<<" "<<mid<<endl;
        MergeSort(x,left,mid);
        //cout<<"mergesort"<<mid+1<<" "<<right<<endl;
        MergeSort(x,mid+1,right);
        Merge(x,y,left,mid,right);
        copy(x,y,left,right);
    }
}

int main()
{
    int n; // 数据量
    cin >> n;
    int x[n];
    for (int i = 0; i < n; i++)
        cin >> x[i];
    MergeSort(x,0,n-1);
    for (int i=0; i<n;i++) cout<<x[i]<<" ";
}

评测

数据规模达到10^9,该算法还是可以满足的。

https://www.luogu.com.cn/problem/P1177

在这里插入图片描述

算法分析

时间复杂度预期O(nlogn),最坏情况O(n^2)。

空间复杂度O(n)。

(3)实现题1-3 最多约数问题

引言

书上这道题与洛谷上的这道题,不同基本一致点在于洛谷的这道题限制了范围,而书上的题目并未设置范围。可以理解为洛谷的题目是书上题目的强化版,故我直接使用洛谷题目来作为实验内容。

题目描述

数学家们喜欢各种类型的有奇怪特性的数。例如,他们认为 945 是一个有趣的数,因为它是第一个所有约数之和大于本身的奇数。

为了帮助他们寻找有趣的数,你将写一个程序扫描一定范围内的数,并确定在此范围内约数个数最多的那个数。不幸的是,这个数和给定的范围的都比较大,用简单的方法寻找可能需要较多的运行时间。所以请确定你的算法能在几秒内完成最大范围内的扫描。

输入格式

只有一行,给出扫描的范围,由下界 L和上界 U 确定。满足 2<L<U<10^9。

输出格式

对于给定的范围,输出该范围内约数个数 D 最多的数 P。若有多个,则输出最小的那个。请输出 Between L and U, P has a maximum of D divisors.,其中 L,U,P,D 的含义同前面所述。

样例 #1

样例输入 #1

1000 2000

样例输出 #1

Between 1000 and 2000, 1680 has a maximum of 40 divisors.

做法1(直接遍历求解+比较)

可以直接采用最朴素的想法,遍历这(b-a+1)个数并分别计算它们的约数数量,再储存最多的。

在处理约数数量的时候,采取很朴素的计算方法:从1到sqrt(n)看是否为n的因子。这个算法的时间复杂度应该是nsqrt(n)。

代码
#include <stdio.h>
using namespace std;

int a,b;
int ans = 0;        // 符合要求的数的最大约数
int ans_num = 0;    // 符合要求的数

void func(){//暴力出奇迹
    for(int i=a;i<=b;i++){
        int ret=0;
        for(int j=1;j*j<=i;j++){
            if(i%j==0) ret+=2;
            if(j*j==i) ret--;
        }
        if(ret>ans){
            ans_num=i;
            ans=ret;
        }
    }
}

int main()
{
    scanf("%d %d",&a, &b);
    func();
    printf("Between %d and %d, %lld has a maximum of %lld divisors.\n",a,b,ans_num,ans);
    return 0;
}

评测

使用洛谷评测结果如下:

在这里插入图片描述

很明显看到4个超时,说明对于较大的数据,这个算法有时间复杂度上的缺陷。这个应该是O(n^2)的。

做法2(使用约数定理计算)

可以采用朴素的想法,遍历这(b-a+1)个数并分别计算它们的约数数量,再储存最多的。

但是在处理的时候使用到约数定理。(百度可知)

在这里插入图片描述

这要求我们先维护一个素数表,保存一定范围内的素数,然后再把原数对素数分别进行反复整除,由此得到原数的质因数分解。一旦分解出质因数,其约数数量也就可以通过公式得到。

代码
#include <stdio.h>
using namespace std;

const int max_num = 1000;
int prime[max_num];  // 维护一张质数表
int prime_total = 0; // 质数表中质数的个数
int a, b;
int ans = 0;     // 符合要求的数的最大约数
int ans_num = 0; // 符合要求的数

// 使用筛法筛出一定范围内的质数
void get_primes(int max_prime)
{
    bool flag[max_prime + 1];
    for (int i = 2; i <= max_prime; i++)
        flag[i] = true;
    for (int i = 2; i <= max_prime; i++)
        if (flag[i])
        {
            for (int j = i + i; j <= max_prime; j += i)
                flag[j] = false;
        }
    for (int i = 2; i <= max_prime; i++)
        if (flag[i])
            prime[prime_total++] = i;
    prime_total--;
}

void search()
{
    for (int i = a; i <= b; i++)
    {
        long long now = i;   // 当前待处理数
        long long total = 1; // 当前数的公约数数量
        for (int j = 0; j <= prime_total; j++)
        {
            int num = 1;
            while (now % prime[j] == 0)
            {
                now /= prime[j];
                num++;
            }
            total *= num;
        }
        if (total > ans)
        {
            ans = total;
            ans_num = i;
        }
    }
}

int main()
{
    scanf("%d %d", &a, &b);
    get_primes(100);
    search();
    printf("Between %d and %d, %lld has a maximum of %lld divisors.\n", a, b, ans_num, ans);
    return 0;
}

评测

评测结果如下:

在这里插入图片描述

中间的错误点应该是涉及到较大的质数了(这个问题在后面一个做法中提出,一旦使用约数定理来计算这个,难免会遇到这个问题),可以看到总体上会比之前要好一些。

做法3(约数定理+DFS)

【更大数据量,限制时间】

将寻找在范围内约数的过程看作一个深度优先搜索,某数的不同质因数可以看作不同层,每一层是一个质因数,对于每一层而言,可以选取0个,1个,……直到快要超出右边界为止。

举例来说,对于一个确定的数140

  • 第一层是质数2,取了2个,现在是1*4=4;
  • 第二层是质数3,取了0个;
  • 第三层是质数5,取了1个,现在是4*5=20;
  • 第四层是质数7,取了1个,现在是20*7=140;
  • 总结来说140=2^2+3^0+5^1+7^1

对于一个范围[3,5]

  • 3=2^0+3^1+5^0
  • 4=2^2+3^0+5^0
  • 5=2^0+3^0+5^1

使用这样的方法搜索所有的可能结果,直到所有的结果都会被计算完,然后就可以比较出最大值。由于这种方法试图使用自下而上做出结果,会比之前的方法快。(略去了计算每一个数的质因数的时间)。

代码
#include <stdio.h>
using namespace std;

const int max_num = 1000;
int prime[max_num]; // 维护一张质数表
int prime_total = 0;// 质数表中质数的个数
int a,b;
int ans = 0;        // 符合要求的数的最大约数
int ans_num = 0;    // 符合要求的数

// 使用筛法筛出一定范围内的质数
void get_primes(int max_prime){
    bool flag[max_prime+1];
    for (int i=2;i<=max_prime;i++) flag[i] = true;
    for (int i=2;i<=max_prime;i++)
        if(flag[i]){
            for (int j=i+i; j<=max_prime; j+=i)
                flag[j]=false;
    }
    for (int i=2;i<=max_prime;i++)
        if (flag[i]) prime[prime_total++]=i;
    prime_total--;
}

//对每一个状态进行列举,并比较记录各个状态中最小的部分
void search(int depth,long long num,long long sum)
{
    // 对于合法枚举结果的比较处理 (到达终点)
    if (a<=num && num<=b){
        if (sum>ans) {ans=sum; ans_num=num;}
        else if (sum==ans && num<ans_num) {ans_num=num;}
    }
    if (depth>prime_total) return;  // 搜索超出规定层数,直接返回
    if (num*prime[depth]>b) return; // 如果本层的质数乘上去都超范围了,那么之后的质数乘上去必然超范围,故直接返回,且本层没有结果
    long long new_num = num;
    // 以当前num为基础,分别乘上i个prime[depth],进行深搜
    search(depth+1,num,sum);
    int i=0;
    while(new_num*prime[depth]<=b){
        new_num*=prime[depth];
        i++;
        search(depth+1,new_num,sum*(i+1));
    }
}

int main()
{
    scanf("%d %d",&a, &b);
    get_primes(b);
    search(0,1,1);
    printf("Between %d and %d, %lld has a maximum of %lld divisors.\n",a,b,ans_num,ans);
    return 0;
}

★说明:这段代码能够处理所有情况,但是它的代价就是时间会超很多,主要在于素数表中存在的素数量过多了,要遍历很多素数。如果将get_primes()的输入参数调整为100,能解决大多数问题。这是因为即使对于2*3*5*7*11*13*17*19*23*29的结果也大于10^9。

无法解决的是如果限定a==b(即a与b相差很小),并且其中有一个因数是很大的质数(比如下面评测中遇到的例子)。

这是一个需要权衡的问题,暂时不能找到一种很好的算法把这两个问题同时解决。

评测

最多因子数(https://www.luogu.com.cn/problem/P1221)

使用洛谷评测,会发现有一个点有问题。

在这里插入图片描述

该点是a==b==131074,它的因子是1,2,65537,131074,包含一个很大的质数65537,而我们对质数的考虑最多到100(100往上可能造成TLE)

故对特例进行特判(不得已而为之,照理不能这样做的)

if(a == b && b == 131074){//小小的特判,因为这个数好像撞到了100以上的质数,如果想过的话可以求出更大的质数
        ans_num = 131074,ans = 4;
        printf("Between %lld and %lld, %lld has a maximum of %lld divisors.",a,b,ans_num,ans);
        return 0;
    }

这样的结果可以达到AC

在这里插入图片描述

还有一种方法是在a和b很接近的时候进行暴力求解(参考前面一种方法),根据a和b的接近程度来决定使用哪种方法,这是一种比较好的求解方法了。

实验感悟

只是课上听讲跟实际上手操作还是有差距的,我已经在很多课程上有这个感悟了,算法是一个计科学生不得不掌握的底层技能,还是要多花时间的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringCloud Nacos服务注册中心和配置中心

一、什么是Nacos&#xff1f; 官方介绍是这样的&#xff1a; Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您实现动态服务发现、服务配置管理、服务及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Na…

WEB 3D技术 three.js 3D贺卡(1) 搭建基本项目环境

好 今天 我也是在网上学的 带着大家一起来做个3D贺卡 首先 我们要创建一个vue3的项目、 先创建一个文件夹 装我们的项目 终端执行 vue create 项目名称 例如 我的名字想叫 greetingCards 就是 vue create greetingcards因为这个名录 里面是全部都小写的 然后 下面选择 vue3 …

全网最细RocketMQ源码四:消息存储

看完上一章之后&#xff0c;有没有很好奇&#xff0c;生产者发送完消息之后&#xff0c;server是如何存储&#xff0c;这一章节就来学习 入口 SendMessageProcessor.processRequest private CompletableFuture<RemotingCommand> asyncSendMessage(ChannelHandlerCont…

低频信号发生器

前言 最近我快期末考试了&#xff0c;有点忙着复习。没时间写文章&#xff0c;不过学会了焊接 挺开心的所以买几套。 焊得怎么样这就是我们今天故事的主角“低频信号发生器”&#xff08;由于要用到所以这是购买链接&#xff09; 好&#xff0c;故事开始&#xff1a; 如何将…

如何在Eclipse IDE中安装TestNG插件

目录 使用Eclipse Marketplace安装TestNG插件 通过输入URL安装TestNG 1.点击安装新软件 2.输入URL以安装TestNG 3.遵循正常的安装过程 4.重新启动Eclipse 在Eclipse中安装TestNG插件的视频 在这篇文章中&#xff0c;我们将介绍如何在Eclipse IDE中安装TestNG插件&#x…

个人数据备份方案分享(源自一次悲惨经历)

文章目录 1 起源2 备份架构2.1 生活照片2.2 生活录音2.3 微信文件2.4 工作文件2.5 笔记、影视音乐、书籍 3 使用工具介绍3.1 小米云服务3.2 中国移动云盘3.3 小米移动硬盘&#xff08;1T&#xff09;3.4 FreeFileSync 4 总结 1 起源 本文的灵感源于我个人的一次不幸遭遇&#…

C++I/O流——(3)文件输入/输出(第二节)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 含泪播种的人一定能含笑收获&#xff…

领域驱动设计——DDD领域驱动设计进阶

摘要 进阶篇主要讲解领域事件、DDD 分层架构、几种常见的微服务架构模型以及中台设计思想等内容。如何通过领域事件实现微服务解耦&#xff1f;、怎样进行微服务分层设计&#xff1f;、如何实现层与层之间的服务协作&#xff1f;、通过几种微服务架构模型的对比分析&#xff0…

快速排序【hoare版本】【挖坑法】【双指针法】(数据结构)

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其基本思想为&#xff1a;任取待排序元素序列中 的某元素作为基准值&#xff0c;按照该排序码将待排序集合分割成两子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右子序列中所有元素均…

文件上传进阶绕过(二)4个技巧和靶场实战

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、环境准备 请移步《文件上传靶场实战&#xff1a;upl…

小迪安全第二天

文章目录 一、Web应用&#xff0c;架构搭建二、web应用环境架构类三、web应用安全漏洞分类总结 一、Web应用&#xff0c;架构搭建 #网站搭建前置知识 域名&#xff0c;子域名&#xff0c;dns,http/https,证书等 二、web应用环境架构类 理解不同web应用组成角色功能架构 开发…

基于JavaWeb+BS架构+SpringBoot+Vue+Hadoop短视频流量数据分析与可视化系统的设计和实现

基于JavaWebBS架构SpringBootVueHadoop短视频流量数据分析与可视化系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 目  录 目  录 I 1绪 论 1 1.1开发背景 1 1.2开…

【C++】__declspec含义

目录 一、__declspec(dllexport)如果这篇文章对你有所帮助&#xff0c;渴望获得你的一个点赞&#xff01; 一、__declspec(dllexport) __declspec(dllexport) 是 Microsoft Visual C 编译器提供的一个扩展&#xff0c;用于指示一个函数或变量在 DLL&#xff08;动态链接库&…

VueCli-自定义创建项目

参考 1.安装脚手架 (已安装可以跳过) npm i vue/cli -g2.创建项目 vue create 项目名 // 如&#xff1a; vue create dn-demo键盘上下键 - 选择自定义选型 Vue CLI v5.0.8 ? Please pick a preset:Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint) > M…

OpenHarmony——Linux之IR驱动

Linux之IR驱动 背景 在光谱中波长自760nm至400um的电磁波称为红外线&#xff0c;它是一种不可见光。红外遥控成本很低&#xff0c;以前广泛应用在电视&#xff0c;空调等电器的控制上面&#xff0c;现在随着蓝牙遥控器慢慢普及&#xff0c;红外遥控越来越少&#xff0c;但在某…

leetcode 142 环形链表II

题目 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使…

阳光抑郁症测试

大部分人对抑郁症的理解&#xff0c;就是每天无精打采&#xff0c;死气沉沉&#xff0c;可实际上&#xff0c;还有一种阳光抑郁症&#xff0c;完全不是这个样子。这种抑郁症的人&#xff0c;做事情非常有活力&#xff0c;魅力十足&#xff0c;给人感觉十分有自信&#xff0c;但…

2024Flutter岗位面试题总结

StatelessWidget和StatefulWidget的区别是什么&#xff1f; StatelessWidget是一个不可变的类&#xff0c;充当UI布局中某些部分的蓝图&#xff0c;当某个组件在显示期间不需要改变&#xff0c;或者说没有状态&#xff08;State&#xff09;&#xff0c;你可以使用它。 Statef…

什么是DDOS高防ip?DDOS高防ip是怎么防护攻击的

随着互联网的快速发展&#xff0c;网络安全问题日益突出&#xff0c;DDoS攻击和CC攻击等网络威胁对企业和网站的正常运营造成了巨大的威胁。为了解决这些问题&#xff0c;高防IP作为一种网络安全服务应运而生。高防IP通过实时监测和分析流量&#xff0c;识别和拦截恶意流量&…

MySQL 协议(非常详细适合小白学习)

MySQL 查询过程 MySQL 查询过程大致如下&#xff1a; 1&#xff09;客户端与服务器端建立连接&#xff1b; 2&#xff09;客户端登陆 MySQL&#xff1b; 3&#xff09;客户端向服务器端发起一条请求&#xff1b; 4&#xff09;服务器端先检查查询缓存&#xff0c;如果命中缓…