介绍:
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
实现:
1、 在有向图中选一个没有前驱的顶点并且输出
2、从图中删除该顶点和所有出边
3、重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
基础模板题:
题意:给一个具有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;
}
题意:给一个有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;
}
题意:给一个具有n个点m条边的有向有权图,找一条最长路;输出最大权值
分析:我们可以对边进行拓扑排序,对于一张图我们可以划分成一些子图,每次维护子图的最大值即可
如下图:对于1-->3 和2-->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;
}
题意:给一个有向图,问最多删去一条边后,图中是否还具有环。
分析:枚举删去一条边后再拓扑排序判断图是否还具有环
#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;
}