拓扑排序就是把有向无环图变成一种线性的序列,这种线性序列有前后关系。如果用队列来存储最后的结果,如果队列中的数据与总的数据n不相等,则说明有环。
此拓扑排序的思想是:
(1)从有向图中选取一个没有前驱的顶点,并输出之;
(2)从有向图中删去此顶点以及所有以它为尾的弧;
(3)如果入度没有为0的表示有环,这时候如果还是向遍历,可以自己加一个起点,然后连接到任意一个图节点即可。
拓扑排序简易代码:
queue<int>q; //priority_queue<int,vector<int>,greater<int>>q; //优先队列的话,会按照数值大小有顺序的输出 //此处为了理解,暂时就用简单队列 TopoSort() { for(inti=1;i<=n;i++) { if(indegree[i]==0) { q.push(i); } } inttemp; while(!q.empty()) { temp=q.front();//如果是优先队列,这里可以是top() printf("%d->",temp); q.pop(); for(inti=1;i<=n;i++)//遍历从temp出发的每一条边,入度-- { if(map[temp][i]) { indegree[i]--; if(indegree[i]==0)q.push(i); } } } }
hdu今天竟然关闭了,只能自己先随便写写了:
使用了数组存储图的信息:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<iterator> using namespace std; int Map[100][100]; int indegree[100]; queue<int> resq; void TopoSort(int n) { queue<int> temq; while(!resq.empty()) { resq.pop(); } for(int i=0;i<n;++i) { if(indegree[i]==0) { temq.push(i); } } while(!temq.empty()) { int temp = temq.front(); temq.pop(); ++cnt; resq.push(temp); for(int i=0;i<n;++i) { if(Map[temp][i]) { --indegree[i]; if(indegree[i]==0) { temq.push(i); } } } } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { memset(Map,0,sizeof(Map)); memset(indegree,0,sizeof(indegree)); for(int i=0;i<m;++i) { int a1,a2; scanf("%d%d",&a1,&a2); if(Map[a1][a2]==0) { Map[a1][a2] = 1; ++indegree[a2]; } } printf("answer: "); TopoSort(n); while(!resq.empty()) { int a = resq.front(); resq.pop(); printf("%d ",a); } printf(" "); } return 0; } /* 5 4 0 1 1 2 1 4 3 1 */
hdu1285,它拓扑的时候还要从小到大排序。有一个输入也是坑,有重复数据。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; int Map[501][501]; int indegree[501]; queue<int> qi; void TopoSort(int n) { //优先队列 priority_queue<int,vector<int>,greater<int> > q; for(int i=1;i<=n;++i) { if(indegree[i]==0) q.push(i); } while(!q.empty()) { int top = q.top(); q.pop(); qi.push(top); for(int i=1;i<=n;++i) if(Map[top][i]) { --indegree[i]; if(indegree[i]==0) q.push(i); } } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { memset(Map,0,sizeof(Map)); memset(indegree,0,sizeof(indegree)); for(int i=0;i<m;++i) { int win,lose; scanf("%d%d",&win,&lose); //不加会报错,测试数据会有重复!!! if(Map[win][lose]==0) { Map[win][lose] = 1; ++indegree[lose]; } } TopoSort(n); while(!qi.empty()) { printf("%d",qi.front()); qi.pop(); if(!qi.empty()) printf(" "); } printf(" "); } return 0; }
此题的map不能使用数组,不然内存会过不去,使用vector。拓扑顺序跟输入相反。
这次测试数据没有重复数据,并且需要判断是否有环。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<map> using namespace std; vector<int> Map[10001]; int indegree[10001]; int tcost; map<int,int> mii; void init() { for(int i=0;i<=10000;++i) Map[i].clear(); memset(indegree,0,sizeof(indegree)); tcost = 0; mii.clear(); } void TopoSort(int n) { //暂时,结果队列 queue<int> qt,qr; int len,nextlen,price; len = nextlen = 0; price = 888; for(int i=1;i<=n;++i) if(indegree[i]==0) { qt.push(i); tcost += price; ++len; } ++price; while(!qt.empty()) { int top = qt.front(); qt.pop(); qr.push(top); --len; for(int i=0;i<Map[top].size();++i) { --indegree[Map[top][i]]; if(indegree[Map[top][i]]==0) { ++nextlen; tcost += price; qt.push(Map[top][i]); } } if(len == 0) { len = nextlen; nextlen = 0; ++price; } } if(qr.size()!=n) { tcost = -1; } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { init(); for(int i=0;i<m;++i) { int a,b; scanf("%d%d",&a,&b); //反向 pair<map<int,int>::iterator,bool> mib = mii.insert(pair<int,int>(a+10000*b,a)); //去重复数据,虽然测试数据没有重复数据 if(mib.second) { ++indegree[a]; Map[b].push_back(a); } } TopoSort(n); printf("%d ",tcost); } return 0; }
hdu3342,标准拓扑排序题,很简单,判断是否有环就行了。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int Map[101][101]; int indegree[101]; void TopoSort(int n) { queue<int> qtemp,qres; for(int i=0;i<n;++i) if(indegree[i]==0) qtemp.push(i); while(!qtemp.empty()) { int top = qtemp.front(); qtemp.pop(); qres.push(top); for(int i=0;i<n;++i) if(Map[top][i]) { --indegree[i]; if(indegree[i]==0) qtemp.push(i); } } if(qres.size()==n) printf("YES "); else printf("NO "); } int main() { int n,m; while(~scanf("%d%d",&n,&m),n) { memset(Map,0,sizeof(Map)); memset(indegree,0,sizeof(indegree)); for(int i=0;i<m;++i) { int mas,pre; scanf("%d%d",&mas,&pre); if(Map[mas][pre]==0) { Map[mas][pre] = 1; ++indegree[pre]; } } TopoSort(n); } return 0; }
hdu4857,这道题目比较坑的是一个数据:
1
3 1
3 1
答案是3 1 2,而我输出2 3 1。
因此需要逆序拓扑。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<stack> #include<map> using namespace std; vector<int> vi[30001]; int indegree[30001]; map<int,int> mii; void init(int n) { for(int i=0;i<=n;++i) vi[i].clear(); memset(indegree,0,sizeof(indegree)); mii.clear(); } void TopoSort(int n) { priority_queue<int,vector<int>,less<int> > qtemp; stack<int> si; int cnt = 0; for(int i=1;i<=n;++i) if(indegree[i]==0) { qtemp.push(i); } while(!qtemp.empty()) { int top = qtemp.top(); qtemp.pop(); si.push(top); int iSize = vi[top].size(); for(int i=0;i<iSize;++i) { int data = vi[top][i]; --indegree[data]; if(indegree[data]==0) qtemp.push(data); } } while(!si.empty()) { printf("%d",si.top()); si.pop(); if(!si.empty()) printf(" "); } printf(" "); } int main() { int t,n,m; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(n); for(int i=0;i<m;++i) { int a,b; scanf("%d%d",&a,&b); pair<map<int,int>::iterator,bool> pmb = mii.insert(pair<int,int>(a*30000+b,a)); if(pmb.second) { vi[b].push_back(a); ++indegree[a]; } } TopoSort(n); } return 0; }