2021-05-12

L2-038 病毒溯源 (25 分)
在这里插入图片描述

病毒容易发生变异。某种病毒可以通过突变产生若干变异的毒株,而这些变异的病毒又可能被诱发突变产生第二代变异,如此继续不断变化。

现给定一些病毒之间的变异关系,要求你找出其中最长的一条变异链。

在此假设给出的变异都是由突变引起的,不考虑复杂的基因重组变异问题 —— 即每一种病毒都是由唯一的一种病毒突变而来,并且不存在循环变异的情况。

输入格式:
输入在第一行中给出一个正整数 N(≤10
​4
​​ ),即病毒种类的总数。于是我们将所有病毒从 0 到 N−1 进行编号。

随后 N 行,每行按以下格式描述一种病毒的变异情况:

k 变异株1 …… 变异株k
其中 k 是该病毒产生的变异毒株的种类数,后面跟着每种变异株的编号。第 i 行对应编号为 i 的病毒(0≤i<N)。题目保证病毒源头有且仅有一个。

输出格式:
首先输出从源头开始最长变异链的长度。

在第二行中输出从源头开始最长的一条变异链,编号间以 1 个空格分隔,行首尾不得有多余空格。如果最长链不唯一,则输出最小序列。

注:我们称序列 { a
​1
​​ ,⋯,a
​n
​​ } 比序列 { b
​1
​​ ,⋯,b
​n
​​ } “小”,如果存在 1≤k≤n 满足 a
​i
​​ =b
​i
​​ 对所有 i<k 成立,且 a
​k
​​ <b
​k
​​ 。

输入样例:
10
3 6 4 8
0
0
0
2 5 9
0
1 7
1 2
0
2 3 1
输出样例:
4
0 4 9 1
本题较难,一看就是用并查集或dfs。题意:病毒代代相传,源头只有一个(祖先就一个),求最长链。当时比赛时感觉会用dfs,但时间问题感觉写不出来,就用了并查集,得到20分(没有加入同长度链的比较)。后来用并查集加入了同长度链比较,结果只得了16分。。。多了一个内存超限错误。下面上代码:
20分:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll nl=1e5+5;
ll p[nl];
ll b[nl]={0};
ll c[nl];
vector<ll>v[nl];
void init(){
	for(ll i=0;i<nl;i++){
		p[i]=i;
	}
}
ll num=1;
ll find(ll x){
	while(x!=p[x]){
		num++;
		x=p[x];
	}
	return num;
}
int main(){
	init();
	ll n;
	cin>>n;
	ll i,j;
	for(i=0;i<n;i++){
		ll k;
		cin>>k;
		while(k--){
			ll x;
			cin>>x;
			p[x]=i;
			b[x]=1;
		}
	}
	ll a=0;
	ll b;
	for(i=0;i<n;i++){
		num=1;//初始化num,不然find一下就改变了
		if(a<find(i)){
			num=1;//同理,不可缺少
			a=find(i);
			b=i;
		}
	}
	cout<<a<<endl;
	//cout<<b<<endl;
	i=0;
	while(b!=p[b]){//这里并没有进行同链长的排序
		c[i]=b;
		b=p[b];
		i++;
	}
	c[i]=b;//别忘记记录b==p[b]时的值。
	//cout<<i<<endl;
	cout<<c[i];
	for(j=i-1;j>=0;j--){
		cout<<" "<<c[j];
	}
}

16分代码:多了内存超限的错误

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll nl=1e5+5;
ll p[nl];
ll b[nl]={0};
ll c[nl];
vector<ll>v[nl];
void init(){
	for(ll i=0;i<nl;i++){
		p[i]=i;
	}
}
ll num=1;
ll i,j;
ll find(ll x){
	while(x!=p[x]){
		v[i].push_back(x);
		num++;
		x=p[x];
	}
	v[i].push_back(x);
	return num;
}
bool cmp(vector<ll>a,vector<ll>b){//进行了同链长的排序
	if(a.size()!=b.size()){
		return a.size()>b.size();
	}else if(a!=b){
		return a<b;
	}
}
int main(){
	init();
	ll n;
	cin>>n;
	//ll i,j;
	for(i=0;i<n;i++){
		ll k;
		cin>>k;
		while(k--){
			ll x;
			cin>>x;
			p[x]=i;
			b[x]=1;
		}
    }
	ll a=0;
	ll b;
	for(i=0;i<n;i++){
		//v[i].push_back(i);
		//cout<<find(i)<<endl;
		num=1;
		if(a<find(i)){
			num=1;
			a=find(i);
			b=i;
		}
	}
	cout<<a<<endl;
	sort(v,v+n,cmp);
	cout<<v[0][a-1];
	for(i=a-2;i>=0;i--){
		cout<<" "<<v[0][i];
	}
}

25分代码:dfs代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll nl=1e4+5;
ll a[nl]={0};//用于查找源点
ll b[nl][nl]={0};//记录两病毒之间是否有亲属关系
vector<ll>v,ve;//用于记录最长链,vector的好用之处,它可以直接对序列进行比较***
ll n,maxn;//maxn即是最长距离
void dfs(ll x,ll y){
	if(y>maxn){
		maxn=y;
		ve=v;
	}else if(y==maxn){
		if(v<ve){//真的好用,省去很多代码
			ve=v;
		}
	}
		for(ll i=0;i<n;i++){
			if(b[x][i]==1){
				v.push_back(i);//插入一个新的点
				dfs(i,y+1);//让新的序列去递归
				v.pop_back(); //在删去此点,因为病毒可以衍生多个不同的新病毒为其他点腾地方
			}
		}
}
int main(){
	cin>>n;
	ll i,j;
	for(i=0;i<n;i++){
		ll k;
		cin>>k;
		while(k--){
			ll x;
			cin>>x;
			a[x]=1;
			b[i][x]=1;
		}
	}
	ll u=0;
	for(i=0;i<n;i++){
		if(a[i]==0){
			u=i;//找到源点
			break;
		}
	}
	v.push_back(u);//初始化v
	dfs(u,1);
	cout<<ve.size()<<endl;
	for(i=0;i<ve.size();i++){
		if(i==0){
			cout<<ve[0];
		}else{
			cout<<" "<<ve[i];
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值