模拟算法题练习(二、DNA序列修正、无尽的石头)

news2024/11/17 19:34:27

(一、DNA序列修正)

问题描述
在生物学中,DNA序列的相似性常被用来研究物种间的亲缘关系。现在我们有两条 DNA序列,每条序列由 A、C、G、T 四种字符组成,长度相同。但是现在我们记录的 DNA序列存在错误,为了严格满足 DNA 序列的碱基互补配对即 A-T和C-G,我们需要依据第一条 DNA 序列对第二条 DNA 序列进行以下操作:
1.选择第二条 DNA 序列的任意两个位置,交换他们的字符,
2.选择第二条 DNA 序列任意一个位置,将其字符替换为 A、C、G、T 中的任何一个。
需要注意的是:每个位置上的碱基只能被操作一次!
你的任务是通过最小的操作次数,使第二条 DNA 序列和第一条DNA序列互补。并且已知初始两条 DNA 序列长度均为 N。
输入格式
第一行包含一个整数 N,(1 ≤ N ≤ 103),表示 DNA 序列的长度。
接下来的两行,每行包含一个长度为 N 的字符串,表示两条 DNA序列。
输出格式
输出一个整数,表示让第二条 DNA 序列和第一条 DNA 序列互补所需的最小操作次数。

2 2 0 0 3 -1 0 1

样例输出:

2 1

解法一:

问题分析

给定两条 DNA 序列,我们需要将第二条 DNA 序列修改为与第一条 DNA 序列互补。碱基互补规则是 A 与 T 互补,C 与 G 互补。同时,任何一个位置只能操作一次。

方法实现

我们可以考虑贪心的思路,因为每次修改操作只能修正一个位置,就是操作和得分比是 1:1;如果我们考虑通过交换来同时修正两个位置,那么操作和得分比就是 1:2,我们应当尽可能多地使用该操作。那么整个过程就是:

  1. 从左到右扫描第一条 DNA 序列和第二条 DNA 序列的每一个位置,检查它们是否互补。

  2. 如果某个位置不互补,我们需要寻找第二条 DNA 序列中后续位置的碱基,看是否可以通过交换使这两个位置都互补。如果可以,我们就进行交换。

  3. 如果在后续位置找不到可以交换的碱基,说明这个位置只能通过替换来满足要求。因为每个位置只能修改一次,所以我们不能把不配对的碱基交换到当前位置作为中转站,则只能进行修改。

  4. 每次交换或替换,操作计数器增加 1。

  5. 最后输出操作计数器的值。

时间复杂度和空间复杂度分析

时间复杂度:O(N2)。在最坏情况下,我们可能需要为每个位置在之后的所有位置中查找可以交换的碱基。

空间复杂度:O(N)。主要是由于输入的两个字符串。

#define  _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std; // 使用std命名空间,以便直接使用cout、cin等,而不是std::cout、std::cin

// 映射表,将字符映射到对应的整数值,A->0, C->1, G->2, T->3
map<char, int>mp{
	{'A',0},
	{'C',1},
	{'G',2},
	{'T',3}
};

int main()
{
	int n;
	cin >> n;
	string a, b;
	cin >> a >> b;
	int cnt = 0; for (int i = 0; i < n; ++i)
	{
		if (mp[a[i]] + mp[b[i]] != 3)
		{
            // 从当前位置之后开始遍历 b 字符串
			for (int j = i + 1; j < n; ++j)
			{
                // 如果交换后可以使得两个字符组合为 "AT" 或者 "CG"
				if (mp[a[i]] + mp[b[j]] == 3 && mp[a[j]] + mp[b[i]] == 3)
				{
					swap(b[i], b[j]);
					break;
				}
			}
			cnt++;
		}
	}
	cout << cnt << endl;

	return 0;
}

解法二:

#include <bits/stdc++.h>

//需要注意的是:每个位置上的碱基只能被操作一次
// 对于操作二, 一定能一次让一个位置变正确
// 对于操作一, 有可能是两/一/零个位置变正确

// 尽量使用操作一, 使两个位置同时变正确

void solve(const int &Case) {
    int n;
    std::cin >> n;
    std::string s1, s2;
    std::cin >> s1 >> s2;
    // A-T C-G
    for (int i = 0; i < n; i++) { // 把序列一转成互补的序列
        if (s1[i] == 'A')s1[i] = 'T';
        else if (s1[i] == 'T')s1[i] = 'A';
        else if (s1[i] == 'C')s1[i] = 'G';
        else s1[i] = 'C';
    }
    int ans = 0;
    for (int i = 0; i < n; i++) { 
        if (s1[i] == s2[i])continue; // 如果当前位置已经正确, 则不需要动
        for (int j = 0; j < n; j++) {
            if (s1[i] == s2[j] && s1[j] == s2[i]) { // 如果一次操作能正确两个位置
                std::swap(s1[i], s1[j]);// 交换, 执行操作一
                break;
            }
        }
        ans++;
    }
    std::cout << ans << '\n';
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int T = 1;
    for (int i = 1; i <= T; i++)solve(i);
    return 0;
}

(二、无尽的石头)

用户登录

问题描述
在一个古老的迷宫中,有一道无尽的通道。通道上每隔一定的距离就会有一块神秘的石头,石头上刻着从1开始的连续整数。从1号石头开始,每块石头的编号都比前一块大 1。
石头上的数字有特殊的意义。如果你站在编号为 几的石头上,并向前走,你将会瞬间移动到编号为几十的石头上,其中为几的各位数字之和。
例如,如果你站在编号为 16 的石头上,由于1+6-7,所以下一步你会移动到编号为 16 +7= 23 的石头上。
现在,会有多次询问,你需要对每个询问输出从1号石头出发,到达指定编号石头的最少步数,如果无法到达,则输出-1。
输入格式
输入包含一个整数 t,(1 <t< 100),表示有t个询问。接下来t行,每行一个整数 n,(1 ≤ n  ≤ 1e6),表示目标石头的编号。
输出格式
对于每个询问,输出一行,表示从1号石头到达目标石头的最少步数。如果无法到达,输出 -1。

解法一:

此题的解决方案可以进行数学化和形式化的优化。首先,题目所述的情况可以被看做是一个单向图的遍历问题,其中节点由数字标号的石头组成,初始节点为标号为 1 的石头。在此图中,从当前节点 n 移动到下一个节点 n+x 的路径是唯一的,其中 x 是 n 的各位数字之和。

鉴于每次遍历只存在一个可能的路径,求解最短路径的问题实际上变成了确定目标节点是否能够在此唯一路径上被访问到。只需依次模拟每一步遍历过程,如果能够在某一步到达目标节点,那么这一步就是到达目标节点的最短路径长度。但是,如果在某一步遍历时跳过了目标节点,那么目标节点将无法访问,因为无法向较小的节点移动。

对于多次查询,我们可以预处理一段范围内的所有可能访问到的节点,并将这些节点存储在数组中。在这种情况下,数组的索引即为到达该节点所需的步数。每当一个新的查询到来,只需检查目标数字是否存在于数组中。如果存在,数组中的索引就是到达目标的最短步数;否则,目标节点将无法被访问。

#include<bits/stdc++.h>

using namespace std;
#define ll long long

const ll MAX = 1e6;// 设置最大值为1000000
vector<ll> stones;

ll sum_digits(ll n) {
    // 求各个数位数字之和
    ll sum = 0;
    while (n) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}

void preprocess() {
    stones.push_back(1);// 向vector中添加数字1作为起始点  
    while (true) {
        ll next = *--stones.end() + sum_digits(*--stones.end());
        // 计算容器 stones 中倒数第二个元素的值,然后将该值与计算该元素各个数字的和相加,并将结果赋值给变量 next
        if (next <= MAX) {
            stones.push_back(next);
        }
        else {
            break;
        }
    }
}

int main() {
    preprocess(); // 预处理函数,生成 stones 数组
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        // 在vector中查找石头编号n,如果找到,输出其在vector中的位置(从0开始计数)
        auto it = find(stones.begin(), stones.end(), n);
        if (it != stones.end()) {
            cout << it - stones.begin() << endl;// 输出位置
        }
        else {
            cout << -1 << endl;
        }
    }
    return 0;
}

解法二:

#include <bits/stdc++.h>

/*
考虑 ai 表示从 1 走到 i 的步数,若走不到则是 ?1。一个明显的递推
方式便是,若 ai = 1,则 ai+f(i) = ai + 1,其中 f(i) 表示 i 的十进制数
位和。预处理除 a 即可询问 O(1) 回答。
时间复杂度 O(n + T)。
*/

std::vector<int> a(1'000'000 + 1, -1);

//1 2 4 8 ......

void solve(const int &Case) {
    int n;
    std::cin >> n;
    std::cout << a[n] << '\n';
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    // 156423
    // 1 + 5 + 6 + 4 + 2 + 3
    // 3
    // 156432 / 10 = 15642
    // 15642 / 10 = 1564
    // ...
    auto f = [&](int x) { //求 x 的各个数位和的函数
        int ret = 0;
        while (x > 0) {
            ret += x % 10;// 取出当前的个位数
            x /= 10; //
        }
        return ret;
    };
    // i -> i + f(i)
    a[1] = 0;
    for (int i = 1; i <= 1'000'000; i++) {
        if (a[i] == -1)continue;
        int x = i + f(i);
        if (x > 1'000'000)break; // > 1'000'000 的位置一定是不会询问的
        a[x] = a[i] + 1; // a[i + f(i)] = a[i] + 1
    }
    int T = 1;
    std::cin >> T;
    for (int i = 1; i <= T; i++)solve(i);
    return 0;
}

今天就先到这了!!!

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!

你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。

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

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

相关文章

C语言题目:指针

1. 下面代码的结果是&#xff1a; #include <stdio.h> int i; int main() {i--;if (i > sizeof(i)){printf(">\n");}else{printf("<\n");}return 0; }答案&#xff1a;> 解析&#xff1a; i作为全局变量且在未赋值的情况下初始值为1&…

大数据分布式计算工具Spark数据计算实战讲解(filter方法,distinct方法,sortby方法)

练习案例 # #单词统计计数 from pyspark import SparkConf, SparkContext import os os.environ[pyspark_python] "D:/python/JIESHIQI/python.exe" #创建一个sparkconf类对象 conf SparkConf().setMaster("local[*]").setAppName("test_spark_app…

学不动系列-git-hooks和husky+lintstage

git-hooks 为了保证提交的代码符合规范&#xff0c;可以在上传代码时进行校验。常用husky来协助进行代码提交时的eslint校验。husky是基于git-hooks来实现&#xff0c;在使用husky之前&#xff0c;我们先来研究一下git-hooks。 构建git-hooks测试项目 需要使用git-hooks就需…

日志系统项目实现

日志系统的功能也就是将一条消息格式化后写入到指定位置&#xff0c;这个指定位置一般是文件&#xff0c;显示器&#xff0c;支持拓展到数据库和服务器&#xff0c;后面我们就知道如何实现拓展的了&#xff0c;支持不同的写入方式(同步异步)&#xff0c;同步:业务线程自己写到文…

C语言基础(二)

5.指针 5.1 指针的定义 内存区域中的每字节都对应一个编号&#xff0c;这个编号就是“地址”. 在程序中定义一个变量&#xff0c;在对程序进行编译时&#xff0c;系统就会给这个变量分配内存单元. 按变量地址存取变量值的方式称为“直接访问”&#xff0c;如printf("&qu…

Nodejs+vue汽车保养美容管理系统vscode前后端分离项目

汽车美容保养管理系统后台采用nodejs语言开发,前台页面和后台管理页面使用vue等技术开发,使用MySql作为数据持久化存储工具对汽车美容保养管理系统的用户等角色权限对应的功能等进行存储。采用vsocde集成IDE对汽车美容保养管理系统统进行开发,整合系统的各个模块。 拟开发的汽车…

【数据分享】2000~2023年MOD15A2H 061 叶面积指数LAI数据集

各位同学们好&#xff0c;今天和大伙儿交流的是2000~2013年MOD15A2H 061 LAI数据集。如果大家有下载处理数据等方面的问题&#xff0c;您可以私信或评论。 Myneni, R., Y. Knyazikhin, T. Park. MODIS/Terra Leaf Area Index/FPAR 8-Day L4 Global 500m SIN Grid V061. 2021, d…

Python:关于数据服务中的Web API的设计

搭建类似joinquant、tushare类似的私有数据服务应用&#xff0c;有以下一些点需要注意&#xff1a; 需要说明的是&#xff0c;这里讨论的是web api前后端&#xff0c;当然还有其它方案&#xff0c;thrift&#xff0c;grpc等。因为要考虑到一鱼两吃&#xff0c;本文只探讨web ap…

USB - 设备架构及设备描述符

一、USB的设备架构 USB从上到下分为设备&#xff08;device&#xff09;、配置&#xff08;config&#xff09;、接口&#xff08;interface&#xff09;和端点&#xff08;endpoint&#xff09;四个层次。 Linux的USB设备驱动是绑定到接口上的, 每个接口在主机看来都是一个独…

基于springboot+vue的共享汽车管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

互动多媒体内容的魔法:如何让你的网页活起来

互动多媒体内容的魔法&#xff1a;如何让你的网页活起来 前言 在之前的文章中&#xff0c;我们探讨了网页结构中的基础介绍&#xff0c;本文将介绍如何通过简单的交互增强用户体验&#xff0c;包括图像大小的动态切换&#xff0c;以及音视频内容的播放控制来介绍网页多媒体的具…

Swiper实现轮播效果

swiper官网&#xff1a;https://3.swiper.com.cn/ <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title&…

Redis 之三:发布订阅(pub/sub)

概念介绍 Redis 发布订阅 (pub/sub) 是一种消息通信模式&#xff0c;它允许客户端之间进行异步的消息传递 Redis 客户端可以订阅任意数量的频道。 模型中的角色 在该模型中&#xff0c;有三种角色&#xff1a; 发布者&#xff08;Publisher&#xff09;&#xff1a;负责发送信…

【DIY】TA7376组成的耳机放大电路

用头戴式耳机&#xff0c;尤其是小型耳机听音乐&#xff0c;总感到音乐味不够足&#xff0c;在低频段的效果更差。因此用本机增强耳机的低频特性&#xff0c;并采用立体声反相合成的办法&#xff0c;加上内藏简易矩阵环绕声电路&#xff0c;能获得强劲的低音和在较宽的范围内展…

使用Node.js构建一个简单的聊天机器人

当谈到人工智能&#xff0c;我们往往会想到什么&#xff1f;是智能语音助手、自动回复机器人等。在前端开发领域中&#xff0c;我们也可以利用Node.js来构建一个简单而有趣的聊天机器人。本文将带你一步步实现一个基于Node.js的聊天机器人&#xff0c;并了解其工作原理。 首先…

http模块学习

http模块 客户端&#xff1a;负责消费资源的电脑 服务器&#xff1a;负责对外提供网络资源的电脑&#xff0c;与普通电脑的区别就在于服务器上 安装了web服务器软件。 http模块是Node.js官方提供用来 创建web服务器的模块&#xff0c;通过http模块提供的http.createServer()方…

WPS/Office 好用的Word插件-查找替换

例如&#xff1a;一片文档&#xff1a;…………泰山…………泰&#xff08;少打了山字&#xff09;………… 要是把“泰”查找替换为“泰山”&#xff0c;就会把前面的“泰山”变成“泰山山”&#xff0c;这种问题除了再把“泰山山”查找替换为“泰山”&#xff0c;有没有更简单…

1978-2022年各省农业总产值数据(无缺失)

1978-2022年各省农业总产值数据(无缺失) 1、时间&#xff1a;1978-2022年 2、范围&#xff1a;包括全国31省 3、来源&#xff1a;国家统计J、统计NJ、各省NJ 4、指标解释&#xff1a;农业总产值是一定时期&#xff08;通常为一年&#xff09;内以货币形式表现的农、林、牧、…

RK3568平台开发系列讲解(基础篇)文件私有数据

🚀返回专栏总目录 文章目录 一、文件私有数据二、文件私有数据实验沉淀、分享、成长,让自己和他人都能有所收获!😄 一、文件私有数据 Linux 中并没有明确规定要使用文件私有数据,但是在 linux 驱动源码中,广泛使用了文件私有数据,这是 Linux 驱动遵循的“潜规则”,实…

【三、接口协议与抓包】使用TcpDump + WireShark抓包分析三次握手与四次挥手

你好啊&#xff01;我是山茶&#xff0c;一个持续探索 AI 测试之路的程序员。 对于三次握手与四次挥手的理解与基本讲解如下。 那么具体的请求是什么样的呢&#xff1f;使用 TCPDump WireShark 对 TCP 三次握手与四次挥手展示 协议分析工具 网络监听&#xff1a;TcpDump Wi…