拓扑排序

图论中的拓扑排序:
在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。

先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。

一直做改操作,直到所有的节点都被分离出来。

如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。

简单的说就是:一步步的在原图上去掉入度为0的点。同时把这个点连得边(也就是出度的边)也去掉。

理解起来就是是这样的
演示一下的话就是(回头再加吧)没找的画图工具
那一个题目说一下吧:
有点绕,应该算中的的题型:
B. Ponds

题目大意就是:
找到回路然后计算出回路中的点个数,奇数则加上各个点权值,偶数则不用管。不构成回路的不参加计算。
题目分析:
难点就是利用拓扑排序来去掉分支找出回路。然后用BFS找出哪个对应的回路。对了还有建图的过程。
再细节的说一下吧 。:首先我们把给出的这些边转化成两个单向边。这是建图过程(不细说了),之后就要去度删边了,先找到度为零的孤点直接删去就行,之后就是度为1的具有单边性质(构不成回路)删去时要注意,删这个点之后可能会影响和他相连的另一个点度的变化。所以还要考虑跟他相连的哪个点的度-1;再去判断循环判断,这中用到队列优化,因为我们要存放度为1的点,这样我们就不会漏或者丢点了。这样我们回路也就找出来了,然后我们就要判断每个独立回路中有多少点了,这决定了他要不要成为代价(奇数算偶数扔)。也就是DFS过程,这中间把点的个数,和点权和都要记录下来,为了之后用到。

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include 
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
#include 
#include 
#include 
#include
#include
#include
#include 
#include 
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<ll,ll> pii;
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxm=1e5+1010;
const int maxn=1e4+1010;
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
const int mod=998244353;
const int MOD=10007;

inline int read() {
     
	int x=0;
	bool t=false;
	char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}

/*
vector m1;
vector m2;
priority_queue , greater > mn;//上  小根堆 		小到大
priority_queue , less > mx;//下   	大根堆  	大到小
mapmp;*/

ll n,m,t,l,r,p;
ll sum,num,ans,res,cnt,flag,maxx,minn;
bool visited[maxn];
ll deg[maxn];
ll head[maxn],vis[maxn];

struct node {
     
	ll to;
	ll next;
}vv[maxm];
queue<ll> q;
void add(ll u,ll v){
     
	vv[cnt].to=v;
	vv[cnt].next=head[u];
	head[u]=cnt++;
}

void inte(){
     
	while(!q.empty())///清空队列
    {
     
        q.pop();
    }
    cnt=0;
    ans=0;
    memset(head,-1,sizeof(head));
    memset(deg,0,sizeof(deg));///度为多少
    memset(visited,false,sizeof(visited));///是否走过

	sf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) sf("%lld",&vis[i]);

	for(int i=1;i<=m;i++){
     
			ll u,v;
		sf("%lld%lld",&u,&v);
		add(u,v);
		add(v,u);
		deg[u]++;
		deg[v]++;
	}
	return ;
}
void topsort(){
     
	for(int i=1;i<=n;i++){
     
		if(deg[i]==0){
     ///孤立点
			visited[i]=true;
		}else if(deg[i]==1){
     ///找到出度为一的点;
			visited[i]=true;
			q.push(i);
		}
	}
	while(!q.empty()){
     
		ll tmp=q.front();
		q.pop();
		for(int i=head[tmp];i!=-1;i=vv[i].next){
     
			ll r=vv[i].to;///对应的
			deg[r]--;
			if(deg[r]==0){
     
				visited[r]=true;
			}else if(deg[r]==1){
     
				visited[r]=true;
				q.push(r);
			}
		}
	}

}
void dfs(ll u,ll gen){
     
	visited[u]=true;
	num++;
	res+=vis[u];
	for(int i=head[u];i!=-1;i=vv[i].next){
     
		ll r=vv[i].to;
		if(!visited[r]&&r!=gen){
     
			dfs(r,u);
		}
	}
	return ;
}
#define read read()
int main(){
     
	cin>>t;
	while(t--){
     
		inte();
		topsort();
		sum=0;
		for(int i=1;i<=n;i++){
     
			if(!visited[i]){
     
				num=0;
				res=0;
				dfs(i,0);
				if(num%2==1){
     
					sum+=res;
				}
			}

		}
		pf("%lld\n",sum);
	}

	return 0;
}



前面头文件挺长的,没啥用,可以删掉哦。

找了一个小模板可以看下:

    queue<int>q;
    vector<int>edge[n];
    for(int i=0;i<n;i++)  //n  节点的总数
        if(in[i]==0) q.push(i);  //将入度为0的点入队列
    vector<int>ans;   //ans 为拓扑序列
    while(!q.empty())
    {
     
        int p=q.front(); q.pop(); // 选一个入度为0的点,出队列
        ans.push_back(p);
        for(int i=0;i<edge[p].size();i++)
        {
     
            int y=edge[p][i];
            in[y]--;
            if(in[y]==0)
                q.push(y);  
        }
    }
    if(ans.size()==n)   
    {
     
        for(int i=0;i<ans.size();i++)
            printf( "%d ",ans[i] );
        printf("\n");
    }
    else printf("No Answer!\n");   //  ans 中的长度与n不相等,就说明无拓扑序列

你可能感兴趣的:(拓扑排序)