拓扑排序-HDU1285-HDU2647-HDU2094-HDU1811

来源:互联网 发布:java分前端和后端吗 编辑:程序博客网 时间:2024/05/08 03:07

拓扑排序三部走:

1.找入度为0的点

2.删除该点和该点发出的所有边

3,循环执行1,2

/*(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.(3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.1.选入度为0的点2.删掉该点,并且删掉该点发出的所有边3.循环*/#include <iostream>#include <cstring>#include <vector>#include <queue>#include <algorithm>using namespace std;const int MAX = 10240;int N, M, pDegree[MAX];queue<int> Q;vector<int> pMap[MAX], pVec; //链式存图void TopSort();int main(){cin >> N >> M;memset(pDegree, 0, sizeof(pDegree));for(int i = 1; i <= M; ++i){int s, e;cin >> s >> e;pMap[s].push_back(e);// 有向图++pDegree[e];// 计算入度}TopSort();return 0;}void TopSort(){for(int i = 1; i <= N; ++i){if(pDegree[i] == 0)// 入度为0的点入队{Q.push(i); }}int x;while(!Q.empty()){x = Q.front(); Q.pop();pVec.push_back(x);// 出队顺序即为拓扑序列for(int i = 0; i < pMap[x].size(); ++i){--pDegree[pMap[x][i]];// 删边if(pDegree[pMap[x][i]] == 0)// 新的入度为0的点{ Q.push(pMap[x][i]); }}}int i;for( i = 1; i <= N; i++){if(pDegree[i] != 0)// 若存在入度不为0的点,则存在环{cout << "Exsit Loop" << endl;return;}}for( i = 0; i < pVec.size()-1; i++)// 顺序输出即为拓扑序列{ cout << pVec[i] << " "; }cout << pVec[i] << endl;}

用队列或者DFS优先拓扑排序在多数情况下时间复杂度能够得到改善,但是,HDU1285要求需要按字典序输出结果,

如果用队列,则需要额外的空间进然排好序再压进队列,这样代码即不美观也不高效,倒不如直接用矩阵存图法,用数

组模拟队列的做法.

#include<iostream>using namespace std;int map[505][505];int indegree[505];int res[505];int n,m,x,y;bool TopoSort(){int i,j,k;for(i=1; i<=n; ++i){for(j=1; j<=n; ++j){if( map[i][j]>0 ) //统计结点入度++indegree[j];}}for(i=1; i<=n; ++i){j = 1;while(j <=n && indegree[j]!=0)//第一步:找入度为0的点++j;if(j > n)return false;res[i] = j;--indegree[j];//第二步:删除该点for(k=1; k<=n; ++k)//第二步:删除该点发出的所有边{if( map[j][k] > 0 )--indegree[k];}}return true;}int main(){int i;while(cin >> n >> m ){memset(map,0,sizeof(map));    //memset需要放在while循环内,这里WA了两次memset(indegree,0,sizeof(indegree));memset(res,0,sizeof(res));while(m--){cin >> x >> y;map[x][y] = 1;}if( TopoSort()){for(i=1; i<n; ++i)printf("%d ",res[i]);printf("%d\n",res[i]);}}return 0;}

再看另一道诡异一点的拓扑题:HDU2647.题目大题说的是:BOSS要给员工发钱,但是,有一些员工有要求,他们要比另外

一些员工的奖金高,有一些员工没有要求,即,最低给888就可以.BOSS又不想多花钱,现在,我们需要找一种发奖金方式使

得BOSS给的钱最少.


如上图,显然薪金有三个层次的,只需要付888*5+889*3+890*1就可以.

由上面的拓扑图,我们可以找到偏序关系,如果p的奖金比q少,那么存在一条边p->q,可以由这个关系建立一张图,链式存储法(我试过矩阵存,但是内存超了),而如果p->q,且pq直接相接,则有money(p)+1=money(q),所以,可以用拓扑排序的方式遍历图,就可以找到每个点要付的奖金.

/*(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.(3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.1.选入度为0的点2.删掉该点,并且删掉该点发出的所有边3.循环*/#include <iostream>#include <cstring>#include <vector>#include <queue>#include <algorithm>using namespace std;const int MAX = 10005;int N, M, inDegree[MAX];int Money[MAX];queue<int> Q;vector<int> pMap[MAX], pVec; //链式存图void TopSort(){int i;for( i = 1; i <= N; ++i){if(inDegree[i] == 0)// 入度为0的点入队{Q.push(i); Money[i] = 888;}}int x;while(!Q.empty()){x = Q.front(); Q.pop();for( i = 0; i < pMap[x].size(); ++i){--inDegree[pMap[x][i]];// 删边,邻接点i的入度-1if(inDegree[pMap[x][i]] == 0)// 新的入度为0的点{ Q.push(pMap[x][i]); Money[pMap[x][i]] = Money[x]+1;}}}for( i = 1; i <= N; i++){if(inDegree[i] != 0)// 若存在入度不为0的点,则存在环{cout << "-1" << endl;return;}}int res = 0;for(i=1 ;i <= N; ++i){res += Money[i];}cout << res << endl;  //否则输出}int main(){int i, s, e;while(cin >> N >> M){memset(inDegree, 0, sizeof(inDegree));memset(Money, 0, sizeof(Money));for(i=1; i<=N; ++i)pMap[i].clear();for( i = 1; i <= M; ++i){cin >> s >> e;pMap[e].push_back(s);// 有向图++inDegree[s];// 计算入度}TopSort();}return 0;}

再来一道拓扑题目:HDU2094,要求的是最后的胜利者,由性质可知,如果存在最终的胜者,那么图中的结点有仅且有1个结点的入度为0.抓住这一点,就容易解决了,先建一个图,然后统计每一个结点的入度,再判断是否只存在一个结点入度为0即可.

#include <iostream>#include <string>#include <vector>using namespace std;const int MAX = 1005;int pMap[MAX][MAX]; //矩阵存图int inDegree[MAX];string word[MAX];int N,M,end;int FindWord(const string& w){int i;for ( i=0; i < end; ++i )if( word[i] == w )return i;word[i] = w;return end++;}int main(){int i,w,l,sum;string win,lose;while( cin >> N && N != 0 ) {end = 0;memset(inDegree,0,sizeof(inDegree));memset(pMap,0,sizeof(pMap));memset(word,0,sizeof(word));for ( i = 0; i < N; ++i ){cin >> win >> lose;w = FindWord(win);l = FindWord(lose);if( pMap[w][l] == 0 ){pMap[w][l] = 1;++inDegree[l];}}sum = 0;for( i = 0; i < end; ++i ){if( inDegree[i] == 0 )++sum;}if( sum == 1 )puts("Yes");else puts("No");}return 0;}

一道经典但是有一定难度的并查集+拓扑排序题目HDU1811

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <queue>using namespace std;const int MAXN = 200005;int father[MAXN];int X[MAXN],Y[MAXN];char O[MAXN];vector<int> pMap[MAXN];int pIndegree[MAXN];int n,sumn;int Find(int x){if (x != father[x]){return father[x] = Find(father[x]);//快速定位,x->father = Find()}return x;}bool Union(int x,int y){x = Find(x);y = Find(y);if (x==y)return false;father[y] = x;return true;}inline void init(){for(int i=0; i<n; ++i){father[i]=i;pMap[i].clear();}memset(pIndegree,0,sizeof(pIndegree));}void TopSort(){queue<int> Q;for(int i=0; i<n; ++i){if(pIndegree[i]==0 && Find(i)==i)//入度为0,要写Find(i)==i,避免同一个点被引用两遍Q.push(i);}bool uncertain = false;while(!Q.empty()){if (Q.size()>1)uncertain = true;int now=Q.front();Q.pop();--sumn;for(int i=0; i<pMap[now].size(); ++i){if(--pIndegree[pMap[now][i]] == 0 ) Q.push(pMap[now][i]);}}if (sumn > 0)printf("CONFLICT\n");else if (uncertain)printf("UNCERTAIN\n");elseprintf("OK\n");}int main(){int m,x,y;while(~scanf("%d%d",&n,&m)){init();sumn=n;for(int i=0; i<m; ++i){scanf("%d %c %d",&X[i],&O[i],&Y[i]);if(O[i]=='='){if(Union(X[i], Y[i])) --sumn;}}for(int i=0; i < m; ++i){if(O[i]!='='){x=Find(X[i]);y=Find(Y[i]);if (O[i]=='>'){pMap[x].push_back(y);pIndegree[y]++;}else{pMap[y].push_back(x);pIndegree[x]++;}}}TopSort();}return 0;}




原创粉丝点击