蓝桥杯每日一题(SPFA)

文章讲述了如何使用SPFA算法处理3305作物杂交问题,涉及队列操作、距离更新以及与Dijkstra算法的区别。同时介绍了851和852两个问题,一个是求最短路,另一个是判断是否存在负环。最后讨论了341最优贸易问题,强调了在不同情况下对最值的计算和路径可达性的维护。
摘要由CSDN通过智能技术生成

3305 作物杂交

用另外一个数组存杂交结果,初始结点都为0,遍历所有杂交结果,只要可以减小就加入到队列中。

if条件是距离小于两个max,因为必须要保证其父辈已经产生。

另外注意使用普通队列即可;

和dijkstra中的st数组不同,这里只用来判断是否是在q中。

所以所有的初始结点都要st为1,pop的时候将结点st置为0.

#include<bits/stdc++.h>
using namespace std;
//3305作物杂交
const int N=2010, M=2e5+10;
int e[M],ne[M],h[N],target[M],dis[N],timee[N],idx,st[N];
int n,m,k,t;
queue<int>q;
void add(int x,int y,int c)
{
  e[idx]=y;
  ne[idx]=h[x];
  target[idx]=c;
  h[x]=idx++;
}
void spfa()
{
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int u=target[i];
            if(dis[u]>max(dis[t],dis[e[i]])+max(timee[t],timee[e[i]]))
               {
                   dis[u]=max(dis[t],dis[e[i]])+max(timee[t],timee[e[i]]);
                   if(st[u])continue;
                   else
                   {
                       q.push(u);
                       st[u]=1;
                   }
               }
        }
    }
}
int main()
{
    cin>>n>>m>>k>>t;
    memset(h,-1,sizeof(h));
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=n;i++)//种植时间
    {
        cin>>timee[i];
    }
    for(int i=0;i<m;i++)//初始节点
    {
        int x;
        cin>>x;
        dis[x]=0;
        q.push(x);
        st[x]=1;
    }
    while(k--)
    {
        int x,y,tt;
        cin>>x>>y>>tt;
        add(x,y,tt);
        add(y,x,tt);
    }
    spfa();
    cout<<dis[t]<<endl;

}

851 spfa求最短路

板子还是老出错。

要记得st数组和dis数组的更新;入队时、出队时候、更新时候;

把st的判断和dis的判断搞反了,先看是否可以更新,可更新一定要先更新。

不要看到st在队列中就不更新了。

#include<bits/stdc++.h>
using namespace std;
//851 spfa求最短路
const int N=1e5+10;
int e[N],ne[N],h[N],w[N],idx,dis[N],st[N];
int n,m;
void add(int x,int y,int c)
{
    e[idx]=y;
    ne[idx]=h[x];
    w[idx]=c;
    h[x]=idx++;
}
void spfa()
{
    queue<int>q;
    q.push(1);
    dis[1]=0;
    st[1]=1;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int u=e[i];//结点是谁
            int dist=w[i];//边长是
            //只要这个边可更新就更新不要管是否有st
            if(dis[u]>dis[t]+dist)//没在队列里
            {
                dis[u]=dis[t]+dist;
                if(!st[u])
                {
                    q.push(u);
                    st[u]=1;
                }
            }
        }
    }

}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof(dis));
    memset(dis,0x3f,sizeof(dis));
    while(m--)
    {
        int x,y,c;
        cin>>x>>y>>c;
        add(x,y,c);
    }
    spfa();
    if(dis[n]==1061109567)cout<<"impossible"<<endl;
    else{
    cout<<dis[n]<<endl;
    }
}

852 spfa判断环路

所有结点要先入队;

因为存在一种情况就是不连通。

所以要把所有的节点入队;dis的数值随意。

所以最短路的求法和负环的判断是不可统一的。

#include<bits/stdc++.h>
using namespace std;
// 852. spfa判断负环  
const int N=1e5+10;
int e[N],ne[N],h[N],w[N],idx,dis[N],st[N],cnt[N];
int n,m;
void add(int x,int y,int c)
{
    e[idx]=y;
    ne[idx]=h[x];
    w[idx]=c;
    h[x]=idx++;
}
int spfa()
{
    queue<int>q;
    for(int i=1;i<=n;i++)q.push(i),st[i]=1;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int u=e[i];//结点是谁
            int dist=w[i];//边长是
            //只要这个边可更新就更新不要管是否有st
            if(dis[u]>dis[t]+dist)//没在队列里
            {
                dis[u]=dis[t]+dist;
                cnt[u]=cnt[t]+1;
                if(cnt[u]>=n)return 1;
                if(!st[u])
                {
                    q.push(u);
                    st[u]=1;
                }
            }
        }
    }
return 0;
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof(dis));
    //memset(dis,0x3f,sizeof(dis));
    while(m--)
    {
        int x,y,c;
        cin>>x>>y>>c;
        add(x,y,c);
    }

    if(spfa())cout<<"Yes"<<endl;
    else{
    cout<<"No"<<endl;
    }
}

341 最优贸易

我们要找到两个城市在一个城市里买,另一个城市里卖掉;

y的思路:以某个点为界,求其左边买入的水晶球的最低价格,求其右边卖出的水晶球的最高价格;然后遍历相减;

之前都是在最短路上求sum属性,现在要求min属性。要求i左边的就从0开始走。i右边的就从n开始走,所以要保存两个相反的h数组。

又要保持路是可达的。

有bug不要盲目找错动动脑筋。

#include<bits/stdc++.h>
using namespace std;
// 341 最优贸易
const int N=1e5+10,M=5e5+10;
int e[M],ne[M],rh[N],h[N],w[N],idx,minn[N],stt[N],st[N],maxx[N];
//minn和maxx都是截止到i这个点(而且路可通)的最大最小值。
int n,m;
//笑死开始忘了怎么调用函数了,int h[]即可
void add(int h[],int x,int y)
{
    e[idx]=y;
    ne[idx]=h[x];
    h[x]=idx++;
}

void spfa(int flag)
{
    //用两个数组,minn和maxx 两个队列
    //spfa分别从正着和逆着两条路找截止到某个点的最大值和最小值。
    
  minn[1]=w[1];
  queue<int>q;
  st[1]=1;
  q.push(1);
  maxx[n]=w[n];
  queue<int>qq;
  stt[n]=1;
  qq.push(n);
  if(flag)
  {
      while(q.size())
      {
          int t=q.front();
          q.pop();
          st[t]=0;
          for(int i=h[t];i!=-1;i=ne[i])
          {
              int u=e[i];
              if(minn[u]>min(minn[t],w[u]))
              {
                  minn[u]=min(minn[t],w[u]);//先更新
                  //这个位置一定要注意只要符合条件就更新,只有没有在数组中的才加入数组
                  if(!st[u])
                  {
                  q.push(u);
                  st[u]=1;
                  }
                  
                  
              }
          }
      }
  }
  else
  {
      while(qq.size())
      {
          int t=qq.front();
          qq.pop();
          stt[t]=0;
          for(int i=rh[t];i!=-1;i=ne[i])
          {
              int u=e[i];
              if(maxx[u]<max(maxx[t],w[u]))
              {
                  maxx[u]=max(maxx[t],w[u]);
                  if(!stt[u])
                  {
                   qq.push(u);//只有没有在队列中的时候才向里面放
                   stt[u]=1;
                  }
              }
          }
      }
  }
}
int main()
{
  memset(minn,0x3f,sizeof(minn));
  memset(maxx,-0x3f,sizeof(maxx));
    cin>>n>>m;
    memset(h,-1,sizeof(h));
    memset(rh,-1,sizeof(rh));

    for(int i=1;i<=n;i++)
    {
        cin>>w[i];
    }
    while(m--)
    {
        int x,y,c;
        cin>>x>>y>>c;
        add(h,x,y);
        add(rh,y,x);
        if(c==2)
        {
        add(h,y,x);
        add(rh,x,y);
        }
    }
    spfa(1);
    spfa(0);
    int res=0;
    //遍历求取以某个点为分界点求最大差值
    for(int i=1;i<=n;i++)
    {
        res=max(maxx[i]-minn[i],res);
       // cout<<maxx[i]<<" "<<minn[i]<<endl;
    }
    cout<<res<<endl;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值