图论中的拓扑排序:
在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。
先统计所有节点的入度,对于入度为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 <immintrin.h>
#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 <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
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;
}
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不相等,就说明无拓扑序列