zoukankan      html  css  js  c++  java
  • 日常训练 dfs 之 拓扑排序

    今天被拓扑排序给折磨了一天,主要就是我的一个代码有点小bug,真难找。。。

    先来看看我今天写的题目吧!

    C. Fox And Names

    Fox Ciel is going to publish a paper on FOCS (Foxes Operated Computer Systems, pronounce: "Fox"). She heard a rumor: the authors list on the paper is always sorted in the lexicographical order.

    After checking some examples, she found out that sometimes it wasn't true. On some papers authors' names weren't sorted in lexicographical order in normal sense. But it was always true that after some modification of the order of letters in alphabet, the order of authors becomes lexicographical!

    She wants to know, if there exists an order of letters in Latin alphabet such that the names on the paper she is submitting are following in the lexicographical order. If so, you should find out any such order.

    Lexicographical order is defined in following way. When we compare s and t, first we find the leftmost position with differing characters: si ≠ ti. If there is no such position (i. e. s is a prefix of t or vice versa) the shortest string is less. Otherwise, we compare characters si and tiaccording to their order in alphabet.

    Input

    The first line contains an integer n (1 ≤ n ≤ 100): number of names.

    Each of the following n lines contain one string namei (1 ≤ |namei| ≤ 100), the i-th name. Each name contains only lowercase Latin letters. All names are different.

    Output

    If there exists such order of letters that the given names are sorted lexicographically, output any such order as a permutation of characters 'a'–'z' (i. e. first output the first letter of the modified alphabet, then the second, and so on).

    Otherwise output a single word "Impossible" (without quotes).

    Examples
    input
    Copy
    3
    rivest
    shamir
    adleman
    output
    Copy
    bcdefghijklmnopqrsatuvwxyz
    input
    Copy
    10
    tourist
    petr
    wjmzbmr
    yeputons
    vepifanov
    scottwu
    oooooooooooooooo
    subscriber
    rowdark
    tankengineer
    output
    Copy
    Impossible
    input
    Copy
    10
    petr
    egor
    endagorion
    feferivan
    ilovetanyaromanova
    kostka
    dmitriyh
    maratsnowbear
    bredorjaguarturnik
    cgyforever
    output
    Copy
    aghjlnopefikdmbcqrstuvwxyz
    input
    Copy
    7
    car
    care
    careful
    carefully
    becarefuldontforgetsomething
    otherwiseyouwillbehacked
    goodluck
    output
    Copy
    acbdefhijklmnogpqrstuvwxyz




    这个不是一个特别难的题目,就是一个比较经典的算法,拓扑排序。
    然后我去学习了一下拓扑排序,学了入度表的,比较简单。
    拓扑排序:大概就是你首先要建图,用vector或者链式前向星都可以,然后还要一个in数组,表示这个数的入度,
    注意一下入度表示被指向的箭头数。然后就用一个for循环去找到入度为0的数,放入队列(如果有字典序的要求用优先队列就好了)
    放入队列之后,再存下这个值,并且把这个指向的位置进行的入度减小,然后再判断一下被指向的数要不要放入队列。

    queue<int>q;
    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.top(); 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("
    ");
    }
    else printf("No Answer!
    "); // ans 中的长度与n不相等,就说明无拓扑序列


    这个题目就是一个拓扑的模板题,如果你会的话,应该比较容易想到,

    首先我们对每个i和它之后的每一个字母进行枚举,之后的每一个字母一定要再第i个之后输出,

    所以就可以用vector存图,然后再写一个tp的板子。应该就差不多这样,下面贴代码!

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    vector<int>vec[110];
    char s[110][110];
    int vis[110][110];
    int ou[110];
    
    void tp()
    {
    	priority_queue<int,vector<int>,greater<int>>que;
    	vector<int>ans;
    	for(int i=0;i<26;i++)
    	{
    		if (ou[i] == 0) que.push(i);
    	}
    	while(!que.empty())
    	{
    		int u = que.top(); que.pop();
    		int len = vec[u].size();
    		ans.push_back(u);
    		for(int i=0;i<len;i++)
    		{
    			int e = vec[u][i];
    			ou[e]--;
    			if (ou[e] == 0) que.push(e);
    		}
    	}
    	if (ans.size() < 26) printf("Impossible
    ");
    	else
    	{
    		for (int i = 0; i <26; i++) printf("%c", ans[i]+'a');
    		printf("
    ");
    	}
    }
    
    int main()
    {
    	int n;
    	cin >> n;
    	for (int i = 0; i < n; i++) scanf("%s", s[i]);
    	for(int i=0;i<n;i++)
    	{
    		for(int j=i+1;j<n;j++)
    		{
    			int len1 = strlen(s[i]);
    			int len2 = strlen(s[j]);
    			int p1 = 0, p2 = 0;
    			while (s[i][p1] == s[j][p2] && p1 < len1&&p2 < len2) p1++, p2++;
    			if(p1!=len1&&p2==len2)
    			{
    				printf("Impossible
    ");
    				return 0;
    			}
    			if(p1!=len1&&p2!=len2)
    			{
    				int u = s[i][p1] - 'a', v = s[j][p2] - 'a';
    				if (vis[u][v]) continue;
    				vis[u][v] = 1;
    				vec[u].push_back(v);
    				ou[v]++;
    			}
    		}
    	}
    	tp();
    	return 0;
    }
    

      

    学完这个之后,我就马上写了两个很简单的板子题,从别人博客上找的,练练手。

    确定比赛名次

     HDU - 1285

    有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。 

    Input输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。 
    Output给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。 

    其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。 
    Sample Input

    4 3
    1 2
    2 3
    4 3

    Sample Output

    1 2 4 3





    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    vector<int>vec[550];
    int in[550], n;
    
    void tp()
    {
    	priority_queue<int, vector<int>, greater<int>>que;
    	vector<int>ans;
    	for (int i = 1; i <= n; i++) if (in[i] == 0) que.push(i);
    	while(!que.empty())
    	{
    		int u = que.top(); que.pop();
    		ans.push_back(u);
    		int len = vec[u].size();
    		for(int i=0;i<len;i++)
    		{
    			int v = vec[u][i];
    			in[v]--;
    			if (in[v] == 0) que.push(v);
    		}
    	}
    	int len = ans.size();
    	for (int i = 0; i < len-1; i++) printf("%d ", ans[i]);
    	printf("%d
    ", ans[len - 1]);
    }
    
    int main()
    {
    	int m;
    	while(cin >> n >> m)
    	{
    		for (int i = 0; i <= n; i++) vec[i].clear();
    		memset(in, 0, sizeof(in));
    		for(int i=1;i<=m;i++)
    		{
    			int a, b;
    			cin >> a >> b;
    			vec[a].push_back(b);
    			in[b]++;
    		}
    		tp();
    	}
    	return 0;
    }
    

      

    Genealogical tree

     POJ - 2367 

    The system of Martians' blood relations is confusing enough. Actually, Martians bud when they want and where they want. They gather together in different groups, so that a Martian can have one parent as well as ten. Nobody will be surprised by a hundred of children. Martians have got used to this and their style of life seems to them natural. 
    And in the Planetary Council the confusing genealogical system leads to some embarrassment. There meet the worthiest of Martians, and therefore in order to offend nobody in all of the discussions it is used first to give the floor to the old Martians, than to the younger ones and only than to the most young childless assessors. However, the maintenance of this order really is not a trivial task. Not always Martian knows all of his parents (and there's nothing to tell about his grandparents!). But if by a mistake first speak a grandson and only than his young appearing great-grandfather, this is a real scandal. 
    Your task is to write a program, which would define once and for all, an order that would guarantee that every member of the Council takes the floor earlier than each of his descendants.

    Input

    The first line of the standard input contains an only number N, 1 <= N <= 100 — a number of members of the Martian Planetary Council. According to the centuries-old tradition members of the Council are enumerated with the natural numbers from 1 up to N. Further, there are exactly N lines, moreover, the I-th line contains a list of I-th member's children. The list of children is a sequence of serial numbers of children in a arbitrary order separated by spaces. The list of children may be empty. The list (even if it is empty) ends with 0.

    Output

    The standard output should contain in its only line a sequence of speakers' numbers, separated by spaces. If several sequences satisfy the conditions of the problem, you are to write to the standard output any of them. At least one such sequence always exists.

    Sample Input

    5
    0
    4 5 1 0
    1 0
    5 3 0
    3 0
    

    Sample Output

    2 4 5 3 1



    这个题目就是给你n个人,然后n行,第i行代表第i个人的后代,让你按照辈分从高到低的顺序输出。



    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    vector<int>vec[110];
    int in[110], n;
    void tp()
    {
    	priority_queue<int, vector<int>, greater<int> >que;
    	vector<int>ans;
    	for (int i = 1; i <= n; i++) if (in[i] == 0) que.push(i);
    	while(!que.empty())
    	{
    		int u = que.top(); que.pop();
    		ans.push_back(u);
    		int len = vec[u].size();
    		for(int i=0;i<len;i++)
    		{
    			int v = vec[u][i];
    			in[v]--;
    			if (in[v] == 0) que.push(v);
    		}
    	}
    	int len = ans.size();
    	for (int i = 0; i < len-1; i++) printf("%d ", ans[i]);
    	printf("%d
    ", ans[len - 1]);
    }
    
    int main()
    {
    	cin >> n;
    	for(int i=1;i<=n;i++)
    	{
    		int x;
    		while(cin>>x&&x)
    		{
    			in[x]++;
    			vec[i].push_back(x);
    		}
    	}
    	tp();
    	return 0;
    }
    

      












  • 相关阅读:
    多线程编程(一)
    所谓费曼学习法
    Java 基本数据类型扩充
    好记性不如烂笔头
    Java_面试札记
    Stream替代for-编码五分钟-划水五小时
    为什么启动线程是start方法?
    Java面试札记
    Tree
    手写SpringMVC 框架
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/10685554.html
Copyright © 2011-2022 走看看