作物杂交
原文
版权归属: GabrielxD
本文链接: https://gabrielxd.top/archives/acwing-3305
许可协议: 本文使用《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》协议授权
输出描述
输出一个整数,表示得到目标种子的最短杂交时间。
思路 动态规划 SPFA算法
本题是用 Bellman-Ford 的本质——动态规划来思考,而因为 SPFA 算法是 Bellman-Ford 算法的宽搜优化,所以可以用 SPFA 算法进行优化。
状态转移方程:\(f(i,j)=min(f(i,j),max(f(i−1,x),f(i−1,y))+max(Tx,Ty))\)
按照该状态表示, \(f(n−1,t)\) 就是答案(得到目标种子的最短杂交时间),因为从初始种子迭代到目标种子最多只会经过 \(n−1\) 条边。
时间复杂度:\(O(NK)\) ,按照本题的数据范围是 \(O(2×10^8)\) ,显然会超时,但是加一个小优化就可以过 :如果在某一次迭代中 没有任何作物的最小花费时间被更新 ,那就说明已经找到了最优解。
另外也可以使用 SPFA 算法进行优化,首先因为状态都是从上一层转移过来的,所以动归中第一维的 \(i\) 可以直接被优化掉,其次观察状态转移方程发现:每次只有在 f(x) 或者 f(y) 发生变化后 f(j) 才有可能被更新,所以可以直接利用 SPFA算法进行宽搜优化。
SPFA代码
SPFA的思想是 通过进行两边建边将两个前提作物改为一个为前驱节点、一个为边的权值,进而可以建图。
#include <bits/stdc++.h>
using namespace std;
const int N=2020,M=2e5+10;
int n,m,k,t;
int T[N],K[N];
int h[N],to[M],w[M],pre[M],idx;
void add(int a,int b,int c){
to[idx]=c,w[idx]=b,pre[idx]=h[a],h[a]=idx++;
}
int dist[N];
int vis[N],st[N];
void SPFA(){
memset(dist,0x3f , sizeof dist);
queue<int> q;
for(int i=1;i<=m;i++){
q.push(K[i]),st[K[i]]=1,dist[K[i]]=0;
}
while(q.size()){
int a=q.front();
q.pop();st[a]=0;
for(int i=h[a];i!=-1;i=pre[i]){
int c=to[i],b=w[i];
int dis= max(dist[a],dist[b])+ max(T[b],T[a]);
if(dist[c]>dis){
dist[c]=dis; // 如果原材料有更新 产物就有机会被更新
if(!st[c])
q.push(c),st[c]=1;
}
}
}
}
int main(){
memset(h,-1,sizeof h);
cin>>n>>m>>k>>t;
for(int i=1;i<=n;i++){
int o;cin>>o;T[i]=o;
}
for(int i=1;i<=m;i++){
int o;cin>>o;K[i]=o;
}
for(int i=1;i<=k;i++){
int a, b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
SPFA();
cout<<dist[t]<<endl;
return 0;
}