AcWing 3305. 作物杂交

 

先看看题目,总结下来就是根据已有的种子进行杂交,以最快的速度得到目标种子。

这个题是蓝桥杯的原题,在蓝桥杯练习系统提交的时候,用dfs是可以过的,但是在acwing上是过不了的,好像是因为会出现环。

所以在这里把两种思路都记下来。

1.dfs

dfs的思路比较简单,查找可以杂交得到目标种子的植物a和b,如果a和b没有现成的种子,那么接着查找a和b通过杂交得到的最短时间。

这个思路很简单的,所以也没啥可说的。然后一个需要注意的点是dfs返回的是得到该种子的时间,所以杂交的时候得等到这个种子长成植物之后才可以。

那么我们在计算通过种子a和b得到c的时间就是seed[a] = max(seed[b],seed[c])+max(t[a],t[b]).先同时得到a和b的种子,然后同时种下去。

代码如下:

int T[N];
int K[N];
int flag[N];  //是否得到了这个种子 
int seed[N];  //得到每个种子的最小时间 
vector<int>mp[N];
int dfs(int x){
	for(int i = 0; i < mp[x].size(); i += 2){
		int a = mp[x][i];
		int b = mp[x][i + 1];
		if(!flag[a]) seed[a] = dfs(a);
		if(!flag[b]) seed[b] = dfs(b);
		seed[x] = min(seed[x], max(seed[a], seed[b]) + max(T[a], T[b]));
		flag[x] = 1;
	} 
	return seed[x];
} 
int main(){
	int n, m, k, t;
	cin>>n>>m>>k>>t;
	memset(seed, 0x3f, sizeof(seed));
	for(int i = 1; i <= n; i++){
		cin>>T[i];
	} 
	for(int i = 0; i < m; i++){
		cin>>K[i];
		flag[K[i]] = 1;
		seed[K[i]] = 0; 
	}
	for(int i = 0; i < k; i++){
		int a, b, c;
		cin>>a>>b>>c;
		mp[c].push_back(a);
		mp[c].push_back(b);
	}
	cout<<dfs(t)<<endl;
	return 0;
} 

2.spfa

说实话,如果这道题的算法标签不打上spfa的话,我是肯定想不到的。但是即使打上了,我也还是没明白怎么建图。

下面记录一下我的草率理解。

图中有n个顶点,有m条边,我们在使用邻接表建图的时候,这条边上不但可以存储边权,也可以存储其他的信息,例如这条边相关的两个节点可以生成的新种子。那么我们在建边的时候不但要记录这条边的两个顶点还要记录这两个种子杂交生成的新种子。

然后这个题我是这么理解的,因为我们想要得到生成目标种子的最短时间,相当于走了很多条边然后得到一条到目标种子t的最短路。

但是这个spfa是计算的单源路径的,这个源在哪呢?于是我们想到了前几天做的那道题,我们也虚拟一个超级源点,然后对于每个已有的种子,和这个超级源点0之间的距离就是0。然后我们对现有的边开始枚举,不断更新每个顶点到源点的距离,直到我们得到目标种子的最短时间,也就是和源点之间的最短路径。

然后代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
const int N = 2010;
const int M = 200005;
int h[N], e[M], ne[M], target[M], idx, w[N];
int dis[N]; 
bool st[N];
int n, m, k, t; 
queue<int>q;
void add(int a, int b, int c){
	e[idx] = b;
	target[idx] = c;  //可以杂交生成的种子
	ne[idx] = h[a];
	h[a] = idx++;
} 
void spfa(){
	while(q.size()){
		int x = q.front();
		q.pop();
		st[x] = false;  //出队就标记
		for(int i = h[x]; i != -1; i = ne[i]){
			int y = e[i];
			int z = target[i];
			if(dis[z] > max(dis[x], dis[y]) + max(w[x], w[y])){  //用这两个顶点来更新边上的顶点z
				dis[z] = max(dis[x], dis[y]) + max(w[x], w[y]);
				if(!st[z]){
					q.push(z);
					st[z] = 1;  //入队
				}
			}
		} 
	}
}
int main(){
	scanf("%d%d%d%d", &n, &m, &k, &t);
	memset(dis, 0x3f, sizeof(dis));
	memset(h, -1, sizeof(h));
	for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
	for(int i = 1; i <= m; i++){
		int x;
		scanf("%d", &x);
		dis[x] = 0;  //现有的种子,最短路径为0
		q.push(x);
		st[x] = true;   //如入队了
	}
	for(int i = 1; i <= k; i++){
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
		add(b, a, c);
	} 
	spfa();
	printf("%d\n", dis[t]);
	return 0;
} 

我是真的不懂spfa是什么。先写到这,我饿了。

最近睡得不好,心脏都不好。我明天开始就好好养生,好好学习!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值