作物杂交 -- 蓝桥杯真题详解【一边推思路一边敲代码】

思路:

为什么会将所有已有的种子作为起点呢?

因为题目中说了,你可以从任意一个已有的种子开始杂交,所以你需要考虑所有可能的起点。如果只选择一个起点,可能会错过更优的方案。
所以先将所有已有的种子作为起点,将它们的距离设为0,并加入队列。然后不断从队列中取出一个种子,遍历它能杂交得到的新种子,并更新它们的距离。如果新种子的距离比之前小,就将它加入队列。最后输出目标种子的距离,就是最小时间。

邻接表建图:以边来建图!
边上存的信息是很灵活的,除了存边权,还能存各种会影响更新的其他信息
a–(w,b)–>c , b–(w,a)–>c
需要记录在边上的信息不仅包括边权w[i]=max(t[a],t[b]),
还包含与该点配种的植物编号,用g[]存,则更新方式为:
d[j]=min(d[j],max(d[t],d[g[i]])+w[i])

st数组的作用:
补充: SPFA算法中为什么会有 st[i] = false 和 st[i] = true的反复出现呢?

  1. 这里的st数组是用来记录一个点是否在队列中的。如果st[t]为true,表示点t在队列中,否则表示不在队列中。
  2. 当从队列中取出一个点t时,就要将st[t]设为false,表示它不再在队列中了。然后遍历它能到达的所有点j,如果发现dist[j]可以被更新,就要将j加入队列,并将st[j]设为true,表示它现在在队列中了。
  3. 这样做的目的是为了避免重复加入同一个点到队列中,造成无效操作和浪费时间。如果一个点已经在队列中了,就不需要再次加入了。

代码:

#include 
#include 
#include 
#include 

using namespace std;

const int N = 2e3 + 10, M = 2e5 + 10;   //邻接矩阵,稠密图?应该是吧!但spfa好像是用邻接表
int n, m, k, T;      //输入是:总共的作物种数,现有的作物种数,k表示可以杂交的方案数(边数),生成目标
queue<int> q;
int h[N], e[M], w[N], target[M], ne[M], idx;
int dist[N];    //dist[i]:表示到源点的最短距离
bool st[N];

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())    //队列不为空!
    {
        auto x = q.front(); //取出队头元素,利用队头元素去更新它的邻接点!
        q.pop();
        
        st[x] = false;  //标记不在队列中了
        
        //遍历邻接点:  i 是边的编号!
        for (int i=h[x]; ~i; i = ne[i])
        {
            int y = e[i];   //取出与之杂交的点
            int z = target[i];       //取出目标种子。
            //看目标的距离能否更新!
            if (dist[z] > max(dist[x], dist[y]) + max(w[x], w[y])) 
            {
                dist[z] = max(dist[x], dist[y]) + max(w[x], w[y]);
                if (!st[z])
                {
                    q.push(z);
                    st[z] = true;
                }
            }    
        }
    }
}

int main()
{
    scanf ("%d%d%d%d", &n, &m, &k, &T);
    
    //N种作物的成熟时间!
    for (int i=1; i <= n; i ++) 
        scanf("%d", &w[i]); 
    
    memset (dist, 0x3f, sizeof (dist));
    memset (h, -1, sizeof (h));
    //因为题目说了有多个已知作物,而对于已知作物而言,都是从已知作物为起点出发, 所以存在多个起点。我们不知
    //道从哪个起点开始杂交的距离最短,所以需要通过枚举每个起点,当然了我们也可以通过建立虚拟源点的写法求解。
    //这里采用spfa的特性,将已有种子作为起点,设他们到起点的距离为0,并加入队列,然后不断从队列中取出种子,
    //遍历它能杂交得到的新种子,并更新它们的距离,
    while (m -- )    //m个起点
    {
        int x;
        scanf ("%d", &x);   //输入起点
        dist[x] = 0;
        q.push(x);
        st[x] = true;   //标记该点存在于队列中!
    }
    
    //杂交的方案数:
    while (k --)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);      //建立邻接表!
        add (a, b, c);  //其中a和b杂交生成c
        add (b, a, c);  //无向图
    }
    
    spfa();
    printf ("%d\n", dist[T]);
    return 0;
}

你可能感兴趣的:(图论,#,最短路算法,蓝桥杯,算法,职场和发展)