2021安徽省大学生程序设计竞赛-I题-病毒

题目:

        地球即将毁灭,科学家们派遣了一支队伍前往银河系外进行调查,寻找未来人类能居住的行星。幸运的是,在一个星球上,科学家们搜集到了许多 RNA 片段,科学家们把这些 RNA 片段运送往地球的实验基地中进行研究。经过几周的研究,他们发现这些 RNA 片段中存在着许多未知的病毒!每个 RNA 片段都由 ACT 三种碱基组成,经过研究发现,如果一个 RNA 能够翻译出一个 AACCTT 无限循环的蛋白质序列,那么这个 RNA 就存在病毒!RNA 翻译蛋白质时,首先会随机选择一个位置的碱基,将该碱基翻译为蛋白质,然后继续随机选择相邻位置的碱基进行翻译,直到结束。现在给出一个 RNA,你需要判断这个RNA 是否存在病毒。

输入:

第一行两个整数 N 和 M,N 表示 RNA 中所含碱基总数,M 表示相邻碱基的对数。

接下来一行,给出一个长 N 的字符串,表示每个位置碱基的种类。接下来 M 行,每行两个整数 ai,bi,表示位置 ai 的碱基和位置 bi 的碱基相邻。

1 <= N, M <= 105

1 <= ai, bi <= N

保证字符串中只含 ACT。

输出:

如果存在病毒,输出“Yes”,否则输出“No”

解题思路:

首先根据题目分析可知,RNA片段可以用无向图来存储,判断其是否存在病毒的条件即为判断该无向图中是否存在满足要求的环(环的序列为n个AACCTT序列,n=1,2,3…)

根据题目中描述的RNA片段的特点,显然我们可以用无向图来表示RNA片段:

如:

存在病毒的判定条件:

  能否翻译出一个AACCTT无限循环的蛋白质序列

       分析:

  1. 图中存在环才能满足无限循环

  2. 环的一个周期可以翻译出n个AACCTT序列,n = 1,2,3 . . . 

               即存在环且满足环的序列为AACCTT,AACCTTAACCTT,AACCTTAACCTT…

样例分析:

 

具体解法:

变量与函数设计:

(1)RNA片段图使用邻接表的形式存储,节省空间:

        string Vertex 存储各个顶点(碱基)

        vector<int> g[n+1] 为邻接表,g[i] 存储与顶点 i 相邻的顶点

        string circle=“AACCTT” 为循环体(目标序列)

        int vis[n+1]  标记所有顶点的状态

      0 -> 未访问过  1 ->  起点(头部)    2 -> 访问过被排除掉 

(2)bool is_start( int v ) 判断编号为v的顶点是否满足环起点的要求

(3)bool judge( int v, int n) 递归搜索判环,发现目标环则返回true,否则返回false;参数v为当前顶点编号,参数n为当前匹配序号

具体算法流程:

(1)选择搜索起点,标记匹配序号n=0,开始进行匹配搜索。按照顶点的存储顺序选择起点,环的起点应该是碱基A,且满足未被标记为排除且同时与A与T相连。如图1中的2、6号可选择作为搜索起点。(匹配序号n代表当前匹配目标序列的下标,n==0代表第一位,n==5代表第五位。)

(2)当n==0时为目标序列第一位,可作为环的头部,因此标记当前结点v为头部(vis[v]=1)。如图2中2号顶点被标记为头部(vis[2]=1)

(3)当n==5时,即已匹配到目标序列的尾部,判断其是否与某个头部相连,即遍历邻接点,存在相邻顶点i,使得vis[i]==1,首尾相连则返回true,若不存在,则可能是一个环中有多段目标序列,转第(4)步,进行下一轮匹配

如图3为n==5时未形成环继续匹配,图4为n==5时形成环,判断结束

(4)匹配过程为获取待匹配字母(碱基)next=circle[(n+1)%6],遍历当前顶点的相邻顶点,若相邻顶点未被标记为排除且vj为next,则匹配成功,匹配序号n=(n+1)%6,转到(2)再对vj进行匹配。

(5)匹配失败未发现目标环,将已搜索过的顶点标记为排除,返回false;

复杂度分析:

输入的碱基总数为n,相邻碱基对数为m

空间复杂度:

      邻接表存储顶点占用空间O(n)

      边的存储占用空间O(m)

      总的空间复杂度:O(n)+O(m)

时间复杂度:

      匹配过程中每个顶点只访问一次且每条边最多走一次

      总的时间复杂度为O(n)+O(m)

代码实现:


#include<iostream>
#include<cstring>
#include<vector>
using namespace std;

string Vertex;//顶点
vector<int>* g;//邻接表
int* vis;//访问标记 初始为0,访问为1,排除为2 
string circle = "AACCTT";

//以A为起点
bool is_start(int v) {
	vector<int>::iterator it;
	bool pre_flag = false, suc_flag = false;
	for (it = g[v].begin(); it != g[v].end(); it++)
	{
		if (Vertex[*it - 1] == 'A')
			suc_flag = true;
		if (Vertex[*it - 1] == 'T')
			pre_flag = true;
	}
	if (pre_flag && suc_flag)
		return true;
	else
		return false;
}

//v为当前结点在邻接表中的下标,n为序号
bool judge(int v, int n) {
	if (n == 0) vis[v] = 1;
	char next = circle[(n + 1) % 6];
	vector<int>::iterator it;
	if (n == 5)
		for (it = g[v].begin(); it != g[v].end(); it++)
			if (vis[*it] == 1) return true;
	//num!=5 or num==5&&没有形成环
	for (it = g[v].begin(); it != g[v].end(); it++)
		if (vis[*it]!=2 && Vertex[*it - 1] == next)
			if (judge(*it, (n + 1) % 6)) return true;
	vis[v] = 2;//退回来说明该点不在任何目标环中
	return false;
}

int main()
{
	int n, m;
	cin >> n >> m;
	cin >> Vertex;
	g = new vector<int>[n + 1];
	vis = new int[n + 1];
	for (int i = 0; i < n + 1; i++)
		vis[i] = 0;
	for (int i = 0; i < m; i++) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for (int i = 1; i < n; i++) {
		if (vis[i] == 2 || Vertex[i - 1] != 'A')
			continue;
		if (!is_start(i))
			continue;
		if (judge(i, 0))
		{
			cout << "Yes";
			return 0;
		}
	}
	cout << "No";
	return 0;
}

运行结果:

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

weixin_52845633

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

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

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

打赏作者

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

抵扣说明:

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

余额充值