为什么会将所有已有的种子作为起点呢?
因为题目中说了,你可以从任意一个已有的种子开始杂交,所以你需要考虑所有可能的起点。如果只选择一个起点,可能会错过更优的方案。
所以先将所有已有的种子作为起点,将它们的距离设为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的反复出现呢?
#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;
}