拓扑排序
#include<cstdio> #include<queue> #include<cstring> #include<vector> using namespace std; const int maxm = 105; vector<int> ve[maxm]; int indeg[maxm], n, m, num, a[maxm], x, y; queue<int> q; void topsort() { while(!q.empty())q.pop(); num = 0; for(int i = 1; i <= n; i++) { if(!indeg[i]) q.push(i); } while(!q.empty()) { int nx = q.front(); a[num++] = nx; q.pop(); for(int i = 0; i < ve[nx].size(); i++) { if(--indeg[ ve[nx][i] ] == 0) q.push(ve[nx][i]); } } } //num小于n的话说明有环,因为环中的元素进不去队列。等于n说明没环 int main() { while(~scanf("%d%d", &n, &m)) { if(n == 0 && m == 0) break; memset(indeg, 0, sizeof(indeg)); memset(a, 0, sizeof(a)); for(int i = 1; i <= n; i++) { ve[i].clear(); } while(m--) { scanf("%d%d", &x, &y); ve[x].push_back(y); indeg[y]++; } topsort(); for(int i = 0; i < n; i++) { if(i == 0) printf("%d", a[i]); else printf(" %d", a[i]); } printf("\n"); } }
找入度为零的点,然后一个一个存,一般用于很多任务,给出几个优先级, 然后得到总的顺序。
https://vjudge.net/contest/280900#problem/A习题
难题 https://vjudge.net/contest/280900#problem/H
Sorting It All Out
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 21532 Accepted: 7403
Description
An ascending sorted sequence of distinct values is one in which some form of a less-than operator is used to order the elements from smallest to largest. For example, the sorted sequence A, B, C, D implies that A < B, B < C and C < D. in this problem, we will give you a set of relations of the form A < B and ask you to determine whether a sorted order has been specified or not.
Input
Input consists of multiple problem instances. Each instance starts with a line containing two positive integers n and m. the first value indicated the number of objects to sort, where 2 <= n <= 26. The objects to be sorted will be the first n characters of the uppercase alphabet. The second value m indicates the number of relations of the form A < B which will be given in this problem instance. Next will be m lines, each containing one such relation consisting of three characters: an uppercase letter, the character "<" and a second uppercase letter. No letter will be outside the range of the first n letters of the alphabet. Values of n = m = 0 indicate end of input.
Output
For each problem instance, output consists of one line. This line should be one of the following three:
Sorted sequence determined after xxx relations: yyy...y.
Sorted sequence cannot be determined.
Inconsistency found after xxx relations.
where xxx is the number of relations processed at the time either a sorted sequence is determined or an inconsistency is found, whichever comes first, and yyy...y is the sorted, ascending sequence.
Sample Input
4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0
Sample Output
Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.
分析:
这题典型的拓扑排序,但是有点变化。
题目样例的三种输出分别是:
1. 在第x个关系中可以唯一的确定排序,并输出。
2. 在第x个关系中发现了有回环(Inconsisitency矛盾)
3.全部关系都没有发现上面两种情况,输出第3种.
那么对于给定的m个关系,一个个的读进去,每读进去一次就要进行一次拓扑排序,如果发现情况1和情况2,那么就不用再考虑后面的那些关系了,但是还要继续读完后面的关系(但不处理)。如果读完了所有关系,还没有出现情况1和情况2,那么就输出情况3.
拓扑排序有两种方法,一种是算法导论上的,一种是用贪心的思想,这题用贪心的思想做更好。
贪心的做法:
1. 找到所有入度为0的点, 加入队列Q
2.取出队列Q的一个点,把以这个点为起点,所有它的终点的入度都减1. 如果这个过程中发现经过减1后入度变为0的,把这个点加入队列Q。
3.重复步骤2,直到Q为空。
这个过程中,如果同时有多个点的入度为0,说明不能唯一确定关系。
如果结束之后,所得到的经过排序的点少于点的总数,那么说明有回环。
题目还需要注意的一点:如果边(u,v)之前已经输入过了,那么之后这条边都不再加入。
//这一题的话一开始是想只有n个字母都进去了才能判冲突,但是就算没全进去也有可能有冲突,
//然后因为就算没有全进去也可以用拓扑排序来判断,因为那些还没出现的字母,他们的入度都为零,所以也会进队,所以也可以通过判是否等于n来判断是否有冲突。
//另外一点就是顺序不一定是字母序,严格按照得到的结果来输出。
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; vector<int> ve[30]; int indeg[30], indeg1[30]; int findx(int x, int y){ for(int i = 0; i < ve[x].size(); i++) { if(ve[x][i] == y) return 1; } return 0; } int n, m, x, y, res[30], now; queue<int> q; int topsort() { while(!q.empty()) q.pop(); now = 0; for(int i = 0; i < n; i++) { if(indeg[i] == 0) q.push(i); } int unio = 0; while(!q.empty()) { if(q.size() > 1) unio = 1; //说明有两个入度为零的,则无法确定。 int t = q.front(); res[now++] = t; q.pop(); for(int i = 0; i < ve[t].size(); i++) { if(--indeg[ ve[t][i] ] == 0) { q.push(ve[t][i]); } } } if(now < n) return 1; if(unio) return 2; return 3; } char ch[5]; int main() { while(~scanf("%d%d", &n, &m)) { if(n == 0 && m == 0) break; memset(indeg, 0, sizeof(indeg)); for(int i = 0; i < n; i++) { ve[i].clear(); } int done = 0; int flag = 0; int ant = 0; int stop = -1; while(m--) { scanf("%s", ch); if(done == 1) continue; ant++; x = ch[0] - 'A'; y = ch[2] - 'A'; if(ch[1] == '<') { if(!findx(x, y)) { indeg[y]++; ve[x].push_back(y); } } else { // x = ch[0] - 'A'; // y = ch[2] - 'A'; if(!findx(y, x)) { indeg[x]++; ve[y].push_back(x); } } memcpy(indeg1, indeg, sizeof(indeg)); flag = topsort(); memcpy(indeg, indeg1, sizeof(indeg1)); if(flag != 2) { done = 1; stop = ant; } } if(flag == 1) { printf("Inconsistency found after %d relations.\n", stop); } else if(flag == 3) { printf("Sorted sequence determined after %d relations: ", stop); for(int i = 0; i < now; i++) { printf("%c", 'A' + res[i]); } printf(".\n"); } else { printf("Sorted sequence cannot be determined.\n"); } } return 0; }
Andrew and Taxi
https://vjudge.net/contest/283149#problem/G
题目意思是要n条路,翻转几条路使得没有环,求翻转的边中的最大值,然后要我们最小化这个最大值。
思路:先二分一个最大值出来,然后把这些比他大的边加进来,然后进行拓扑排序来判环,并得到每条边的拓扑序。
(拓扑序的一个重要性质就是拓扑排序之后,那么在有向边中,起点的拓扑序一定小于终点的拓扑序。)
然后如果没环的话往左走,减小最大值。有环往右走,最后可以得到一个答案。
之后对于比他小的,因为不限翻转的次数,所以只要碰到拓扑序反了的就把这条边反过来。
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxm = 1e5 + 5; struct NODE { int u, v, c; } node[maxm]; int indeg[maxm], a[maxm]; vector<int> ve[maxm], ans; int n, m; int top(int x) { memset(indeg, 0, sizeof(indeg)); memset(a, 0, sizeof(a)); for(int i = 1; i <= n; i++) { ve[i].clear(); } for(int i = 1; i <= m; i++) { if(node[i].c > x) { ve[ node[i].u ].push_back(node[i].v); indeg[ node[i].v ]++; } } //for(int i = 1; i <= n; i++) { // printf("%d\n", indeg[i]); //} queue<int> q; int num = 0; for(int i = 1; i <= n; i++) { if(!indeg[i]) { q.push(i); } } while(!q.empty()) { // printf(" is \n"); int now = q.front(); q.pop(); a[now] = num++; // 拓扑序 //printf("%d\n", now); for(int i = 0; i < ve[now].size(); i++) { if(--indeg[ ve[now][i] ] == 0) { q.push(ve[now][i]); } } } if(num == n) return true; //用于判环,还可以判断每一条边的入度是否为零,如果大于等于一个不为零,则有环。 return false; } int main() { scanf("%d%d", &n, &m); int minn = 1e9 + 7; ans.clear(); for(int i = 1; i <= m; i++) { scanf("%d%d%d", &node[i].u, &node[i].v, &node[i].c); } int l = 0, r = minn, mid = 0, res = 0; while(l <= r) { mid = (l + r) / 2; if(top(mid)) { r = mid - 1; res = mid; } else { l = mid + 1; } } top(res); for(int i = 1; i <= m; i++) { if(a[ node[i].u ] > a[ node[i].v ] && node[i].c <= res) { ans.push_back(i); } } printf("%d %d\n", res, ans.size()); for(int i = 0; i < ans.size(); i++) { if(i == 0) printf("%d", ans[i]); else printf(" %d", ans[i]); } printf("\n"); return 0; }