拓扑排序

介绍:
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
实现:
1、 在有向图中选一个没有前驱的顶点并且输出
2、从图中删除该顶点和所有出边
3、重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
step 1
step 2
step 3
step 4
step 5
基础模板题:

1174 : 拓扑排序·一

题意:给一个具有n个点m条有向边的图,判断该图是否存在环
分析:对边拓扑排序,依次删去无入度的边看是否能删完
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 1e5+5;
int t,n,m;
vector<int>vec[maxn];
int In[maxn]; // 记录每个点的入度
bool topsort()
{
    queue<int>q;
    int cnt=0;
    for(int i=1;i<=n;i++)
        if(In[i]==0) q.push(i);// 入度为0的点
    while (!q.empty())
    {
        int node=q.front(); q.pop();
        cnt++;
        for(int i=0;i<vec[node].size();i++){
            // 如果删去Edge(node-->i)后,i点的入度为0,push进队列
            if(--In[vec[node][i]]==0) q.push(vec[node][i]);
        }
    }
    if(cnt==n) return true;
    return false;
}
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        memset(In,0,sizeof(In));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) vec[i].clear();
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            vec[u].push_back(v);
            In[v]++;
        }
        if(topsort()) puts("Correct");
        else puts("Wrong");
    }
    return 0;
}

1175 : 拓扑排序·二

题意:给一个有n个点m条有向边的图,并且k个点具有一个病毒,
      病毒能沿着有向边传染,问最后每个点具有多少个病毒      
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int mod=142857;
const int maxn = 1e5+5;
int n,m,k;
int In[maxn],val[maxn];
vector<int>vec[maxn];
void topsort()
{
    int ans=0;
    queue<int>q;
    for(int i=1;i<=n;i++) if(In[i]==0) q.push(i);
    while (!q.empty())
    {
        int node=q.front(); q.pop();
        for(int i=0;i<vec[node].size();i++){
            if(--In[vec[node][i]]==0) q.push(vec[node][i]);
            val[vec[node][i]]=(val[vec[node][i]]+val[node])%mod;
            //当前节点vec[node][i] 的病毒数等于本身+上一个节点node的病毒数
        }
    }
    for(int i=1;i<=n;i++) ans=(ans+val[i])%mod;
    printf("%d\n",ans);
}
int main()
{
    while (~scanf("%d%d%d",&n,&m,&k))
    {
        memset(val,0,sizeof(val));
        memset(In,0,sizeof(In));

        for(int i=1,pos;i<=k;i++) scanf("%d",&pos),val[pos]++;

        for(int i=1,u,v;i<=m;i++)
            scanf("%d%d",&u,&v),vec[u].push_back(v),In[v]++;
        topsort();
    }
    return 0;
}

Skiing

题意:给一个具有n个点m条边的有向有权图,找一条最长路;输出最大权值
分析:我们可以对边进行拓扑排序,对于一张图我们可以划分成一些子图,每次维护子图的最大值即可
     如下图:对于1-->32-->3 部分,我们可以选出一个最大值w=4加到 节点 3,那么我们就可以
     删去(1-->3) 和(2-->3)这两条边了,因为这部分的最大值已经加到了节点3...

在这里插入图片描述

#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 1e5+5;
int n,m,t;
int dp[maxn],In[maxn];
vector<pair<int,int>>vec[maxn];
void topsort()
{
    queue<int>q;
    for(int i=1;i<=n;i++) if(In[i]==0) q.push(i);
    while (!q.empty())
    {
        int node=q.front(); q.pop();
        for(int i=0;i<vec[node].size();i++){
            if(--In[vec[node][i].first]==0) q.push(vec[node][i].first);
            
            dp[vec[node][i].first]=max(dp[vec[node][i].first],vec[node][i].second+dp[node]);
            
        }
    }
    int ans=-1;
    for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
    printf("%d\n",ans);
}
int main()
{
    scanf("%d",&t);
    while (t--){
        scanf("%d%d",&n,&m);
        memset(In,0,sizeof(In));
        memset(dp,0,sizeof(dp));
        for(int i=0;i<=n;i++) vec[i].clear();
        for(int i=1,u,v,w;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
 
            vec[u].push_back(make_pair(v,w));
            In[v]++;
        }
        topsort();
    }
    return 0;
}

D. Almost Acyclic Graph

题意:给一个有向图,问最多删去一条边后,图中是否还具有环。
分析:枚举删去一条边后再拓扑排序判断图是否还具有环
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <bitset>
#include <vector>
#include<cstring>
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
const int maxn = 1e5+5;
int n,m;
vector<int>vec[maxn];
int In[maxn],Out[maxn];
bool  topsort()
{
    queue<int>q;
    int cnnt=0;
    for(int i=1;i<=n;i++) if(In[i]==0) q.push(i);
    while (!q.empty())
    {
        int node=q.front(); q.pop();
        cnnt++;
        for(int i=0;i<vec[node].size();i++){
            if(--In[vec[node][i]]==0){

                q.push(vec[node][i]);
            }
        }
    }
    if(cnnt==n) return true;
    return false;
}
int u[maxn],v[maxn];
int main()
{
    while (~scanf("%d%d",&n,&m))
    {
        memset(In,0,sizeof(In));
        memset(Out,0,sizeof(Out));
        for(int i=0;i<=n;i++) vec[i].clear();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u[i],&v[i]);
            vec[u[i]].push_back(v[i]);
            In[v[i]]++;
            Out[v[i]]++;
        }
        bool flag=false;
        if(topsort()) {puts("YES");return 0;}
        for(int i=1;i<=n;i++)
        {
            memcpy(In,Out,sizeof(In));
            if(In[i]>=1){
                In[i]--;
                if(topsort()) {flag=true; puts("YES"); break; }
            }
        }
        if(!flag) puts("NO");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值