拓扑排序

前提是你 提交于 2019-12-27 07:15:21
#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;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!