图论基础,最短路,最小生成树专题

一:拓扑排序

1.神经网路

原题链接:https://ac.nowcoder.com/acm/contest/87814/1001

拓扑板子

查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int head[2000000],res,n,m,c[1000000],v[1000000],ru[1000000],chu[1000000];
struct E{
    int t,l,next;
}edge[2000000];
void lin(int x,int y,int z)//链式向前星
{
    edge[++res].t=y;
    edge[res].l=z;
    edge[res].next=head[x];
    head[x]=res;
}
queue<int>q;
signed main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>c[i]>>v[i];
        if(c[i]>0)q.push(i);
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        lin(x,y,z);
        ru[y]++,chu[x]++;
    }
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=edge[i].next)
        {
            ru[edge[i].t]--;
            if(c[x]>0)c[edge[i].t]+=edge[i].l*c[x];
            if(ru[edge[i].t]==0)
            {
                q.push(edge[i].t);
                c[edge[i].t]-=v[edge[i].t];
            }
        }
    }
    int t=0;
    for(int i=1;i<=n;i++)
    {
        if(chu[i]==0 and c[i]>0)cout<<i<<" "<<c[i]<<endl,t=1;
    }
    if(!t)cout<<"NULL";
    return 0;
}

2.菜肴制作

原题链接:https://ac.nowcoder.com/acm/contest/87814/1003

正向建图考虑的因素过多,所以反向建图再利用拓扑即可

查看代码
 #include <bits/stdc++.h>//拓扑排序
#define int long long
using namespace std;
int head[2000000],res,n,m,c[1000000],v[1000000],ru[1000000],chu[1000000],da[1000000];
struct E{
    int t,next;
}edge[2000000];
void lin(int x,int y)//链式向前星
{
    edge[++res].t=y;
    edge[res].next=head[x];
    head[x]=res;
}
priority_queue<int>q;
bool topu()
{
    while(!q.empty())q.pop();
    for(int i=1;i<=n;i++)
    {
        if(ru[i]==0)q.push(i);
    }
    int num=0;
    while(!q.empty())
    {
        int x=da[++num]=q.top();
        q.pop();
        for(int i=head[x];i!=-1;i=edge[i].next)
        {
            ru[edge[i].t]--;
            if(ru[edge[i].t]==0)
            {
                q.push(edge[i].t);
            }
        }
    }
    if(num<n)return false;
    else return true;
}
signed main()//反向建图
{
    int T;
    cin>>T;
    while(T--)
    {
        memset(head,-1,sizeof head);
        memset(ru,0,sizeof ru);
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            cin>>x>>y;
            lin(y,x);
            ru[x]++;
        }
        if(!topu())cout<<"Impossible!"<<endl;
        else
        {
            for(int i=n;i>=1;i--)
            {
                cout<<da[i]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}

二:最短路

1.公交线路

原题链接:https://ac.nowcoder.com/acm/contest/87814/1002

dijkstra做法

查看代码
 #include<bits/stdc++.h>
using namespace std;
const int N=6e6+10;
bool sign[N];
int h[N],e[N],ne[N],w[N],idx,n,m,s,t;
int dis[N];
typedef pair<int,int> PII;
void add(int x,int y,int z)
{
    e[idx]=y;
    w[idx]=z;
    ne[idx]=h[x];
    h[x]=idx++;
}
int djs(int a,int b)
{
    memset(dis,0x3f,sizeof dis);
    dis[a]=0;
    priority_queue<PII,vector<PII>,greater<PII>> p;
    p.push({0,a});
    while(p.size())
    {
        PII t=p.top();
        p.pop();
        int dian=t.second,distance=t.first;
        if(sign[dian])continue;
        sign[dian]=true;
        for(int i=h[dian];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>distance+w[i])
            {
                dis[j]=distance+w[i];
                p.push({dis[j],j});
            }
        }
    }
    if(dis[b]==0x3f3f3f3f)return -1;
    return dis[b];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>s>>t;
    for(int j=1;j<=m;j++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    cout<<djs(s,t);
    return 0;
}

spfa做法

查看代码
 #include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
bool sign[N];
int h[N],e[N],ne[N],w[N],vis[N],idx,n,m,s,t;
int dis[N];
void add(int x,int y,int z)
{
    e[idx]=y;
    w[idx]=z;
    ne[idx]=h[x];
    h[x]=idx++;
}
queue<int>q;
int spfa(int s,int t)
{
    memset(dis,0x3f,sizeof dis);
    memset(vis,0,sizeof vis);
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=h[x];i!=-1;i=ne[i])
        {
            int y=e[i];
            if(dis[y]>dis[x]+w[i])
            {
                dis[y]=dis[x]+w[i];
                if(!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    if(dis[t]>=0x3f3f3f3f)return -1;
    else return dis[t];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>s>>t;
    for(int j=1;j<=m;j++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    cout<<spfa(s,t);
    return 0;
}

2.Meeting

原题链接:https://ac.nowcoder.com/acm/contest/87814/1022

重点是建图,按正常思维建图会t,那么我们就虚拟出一个点与已知点建边,最后将距离除二即可

查看代码
 #include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6;
bool sign[N];
int h[N],e[N],ne[N],w[N],idx,n,m;
int dis[N],dist[N];
typedef pair<int,int> PII;
int a[1000000];
void add(int x,int y,int z)
{
    e[idx]=y;
    w[idx]=z;
    ne[idx]=h[x];
    h[x]=idx++;
}
void djs(int x,int d[])
{
    for(int i=0;i<=n+m;i++)d[i]=LLONG_MAX;
    memset(sign,0,sizeof sign);
    d[x]=0;
    priority_queue<PII,vector<PII>,greater<PII>> p;
    p.push({0,x});
    while(p.size())
    {
        PII t=p.top();
        p.pop();
        int dian=t.second,distance=t.first;
        if(sign[dian])continue;
        sign[dian]=true;
        for(int i=h[dian];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]>distance+w[i])
            {
                d[j]=distance+w[i];
                p.push({d[j],j});
            }
        }
    }
}
signed main()
{
    int t;
    cin>>t;
    int ans=1;
    while(t--)
    {
        memset(h,-1,sizeof h);
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            int time,num;
            cin>>time>>num;
            while(num--)
            {
                int x;
                cin>>x;
                add(x,n+i,time);//不可以团内两两加边,那就每个团虚拟出来一个点,给定的边权就变成了团内点都与这个点相连,只不过时间会翻倍
                add(n+i,x,time);//n+i就是虚化出的点
            }
        }
        cout<<"Case #"<<ans++<<": ";
        djs(1,dis);
        djs(n,dist);
        int mi=1e18;
        for(int i=1;i<=n;i++)
        {
            mi=min(mi,max(dis[i],dist[i]));
        }
        if(mi==1e18)cout<<"Evil John"<<endl;
        else
        {
            cout<<mi/2<<endl;
            for(int i=1;i<=n;i++)
            {
                if(max(dis[i],dist[i])==mi)cout<<i<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}

三:最小生成树

1.挖沟

原题链接:https://ac.nowcoder.com/acm/contest/87814/1007

prim做法

查看代码
 #include<bits/stdc++.h>
#define int long long
using namespace std;
struct lian{
    int t,l,next;
}edge[2000000];
struct node{
  int x,len;
    bool operator<(const node &a) const
    {
        return len>a.len;
    }
};
int res=0,head[2000000];
int sign[2000000];
void add(int x,int y,int z)
{
    edge[res].t=y;
    edge[res].l=z;
    edge[res].next=head[x];
    head[x]=res++;
}
void prim()
{
    priority_queue<node>q;
    sign[1]=1;
    node tmp;
    for(int i=head[1];i!=-1;i=edge[i].next)
    {
        tmp.x=edge[i].t;
        tmp.len=edge[i].l;
        q.push(tmp);
    }
    int sum=0;
    while(!q.empty())
    {
        node tt=q.top();
        q.pop();
        int x=tt.x;
        if(sign[tt.x])continue;
        sign[tt.x]=1;
        sum+=tt.len;
        for(int i=head[x];i!=-1;i=edge[i].next)
        {
            if(sign[edge[i].t])continue;
            tt.x=edge[i].t;
            tt.len=edge[i].l;
            q.push(tt);
        }
    }
    cout<<sum;
}
signed main()
{
    memset(head,-1,sizeof head);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,v;
        cin>>a>>b>>v;
        add(a,b,v);
        add(b,a,v);
    }
    prim();
}

kruskal做法

查看代码
 #include<bits/stdc++.h>//Kruskal算法
#define int long long
using namespace std;
int fa[2000000];
struct node
{
    int x,y,z;
}edge[2000000];
bool cmp(node a,node b)
{
    return a.z<b.z;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        cin>>edge[i].x>>edge[i].y>>edge[i].z;
    }
    sort(edge+1,edge+m+1,cmp);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int fx=find(edge[i].x);
        int fy=find(edge[i].y);
        if(fx==fy)continue;
        ans+=edge[i].z;
        fa[fx]=fy;
    }
    cout<<ans;
    return 0;
}

补充从某点到某点能经过的最大的最小距离值

查看代码
 #include<bits/stdc++.h>//Kruskal算法
#define int long long
using namespace std;
int fa[2000000];
struct node
{
    int x,y,z;
}edge[2000000];
bool cmp(node a,node b)
{
    return a.z<b.z;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        cin>>edge[i].x>>edge[i].y>>edge[i].z;
    }
    int t1,t2;
    cin>>t1>>t2;
    sort(edge+1,edge+m+1,cmp);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int fx=find(edge[i].x);
        int fy=find(edge[i].y);
        if(fx==fy)continue;
        fa[fx]=fy;
        if(find(t1)==find(t2))
        {
            cout<<edge[i].z;
            return 0;
        }
        ans+=edge[i].z;
        
    }
    return 0;
}

2.Forsanken喜欢独一无二的树

原题链接https://ac.nowcoder.com/acm/contest/87814/1009

正常的kruskal做法,利用双指针去重边

查看代码
 #include<bits/stdc++.h>//Kruskal算法
#define int long long
using namespace std;
int fa[2000000];
struct node
{
    int x,y,z;
}edge[2000000];
bool cmp(node a,node b)
{
    return a.z<b.z;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        cin>>edge[i].x>>edge[i].y>>edge[i].z;
    }
    sort(edge+1,edge+m+1,cmp);
    int ans=0;
    for(int i=1;i<=m;)
    {
        int l=i,r=i;
        while(edge[r].z==edge[l].z)r++;
        for(int j=l;j<r;j++)
        {
            int f1=find(edge[j].x),f2=find(edge[j].y);
            if(f1!=f2)ans+=edge[j].z;
        }
        for(int j=l;j<r;j++)
        {
            int f1=find(edge[j].x),f2=find(edge[j].y);
            if(f1!=f2)fa[f1]=f2,ans-=edge[j].z;
        }
        i=r;
    }
    cout<<ans;
    return 0;
}
posted @ 2024-08-03 16:23  伊芙加登  阅读(9)  评论(0编辑  收藏  举报