作物杂交

发布时间 2023-03-28 18:50:51作者: kingwzun

原文
版权归属: GabrielxD
本文链接: https://gabrielxd.top/archives/acwing-3305
许可协议: 本文使用《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》协议授权

image
输出描述
输出一个整数,表示得到目标种子的最短杂交时间。

思路 动态规划 SPFA算法

本题是用 Bellman-Ford 的本质——动态规划来思考,而因为 SPFA 算法是 Bellman-Ford 算法的宽搜优化,所以可以用 SPFA 算法进行优化。
image

状态转移方程:\(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;
}