拓扑排序题目

题目:

题目1:

题目描述:

用小于号"<"来定义两元素之间的关系,并于一个没有重复元素的有序上升序列 从小到大地排列这些元素。
比如说,序列A,B,C,D意味着A<B;B<C;C<D。
在这个问题里,我们会给你一组形如"A<B"的关系,询问你有序序列的合法性或其本身。


输入

输入有多组数据。
每一组数据由其第一行的2个正整数n,m开始。
第一个正整数n代表有n个元素需要排序(2<=n<=26),这些元素一定是按照字母表顺序,从第一个字母(A)开始的n个大写字母。
第二个正整数m代表有m个关系将会在这组数据中被给出。
接下来有m行输入,每一行输入包含3个字符:一个大写字母,一个"<"符号,另一个大写字母。
所有输入的字母不会超出n规定下的,字母表中的字母范围。
当输入的n=m=0时,代表输入结束。


输出

对于每一组数据,输出有一行。输出必须也必定是以下三种情况之一:
Sorted sequence determined after X relations: YYYY.
[友情翻译:有序序列在给出X个条件后可以成立: YYYY.]
Sorted sequence cannot be determined.
[翻译:无法建立有序序列]
Inconsistency found after X relations.
[翻译:在给出X个关系后出现矛盾]

X是一个整数。
YYYY是当前条件下的有序数列,之间没有分隔符与空格。

样例输入

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


样例输出

Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.

思路:

给定一组字母的大小关系,要你判断是否在某一次读入后,能够判断  

1.该字母序列有序,并依次输出;

   2.该序列不能判断是否有序;

   3.该序列字母次序之间有矛盾,即有环存在。

而这三种形式的判断应该遵循这样的顺序:先判断是否有环(3),再判断是否有序(1),最后才能判断是否能得出结果(2)。

注意:对于(2)必须遍历完整个图!!,而(1)和(3)一旦得出结果,对后面的输入就不用做处理了。

AC代码:

#include<bits/stdc++.h>
 
using namespace std;

const int N = 30;

int Map[N][N],indegree[N],temp[N],Stack[N];
char s[N];
int n;
 
int  tuopu()
{
    int i,j;
    int num;
    int tail = -1,now,flag = 1;
    for(i =0; i < n; i ++)//将每个点的入度先用临时数组存起来,因为一会要修改
    {
        temp[i] = indegree[i];
    }
    for(i = 0; i < n; i ++)//把每个入度为0的点删除,遍历n个点(如果能得出结果的话)
    {
        num = 0;//入度为0的点的个数
        for(j = 0; j < n; j ++)
        {
            if(temp[j] == 0)
            {
                now = j;//标记这个入度为0的点
                num ++;
            }
        }
        if(num == 0)  return -1;//没有入度为0的点,证明存在环,关系矛盾,直接返回,得出结果
        if(num > 1)  flag = 0;//入度为0的点的个数大于1,也得不出关系,但现在不能直接返回,因为当前也有可能存在环,只是还没判断到,要等大循环结束
        Stack[++tail] = now;//当前只有一个入度为0的点,有可能能得出结果,先把当前的信息存起来
        temp[now] = -1;//此入度为0的点标记为-1,避免再次判断
        for(j = 0; j < n; j ++)//把此点连接的点入度减1
        {
            if(Map[now][j]==1)  temp[j] --;
        }
    }
    return flag;
}
 
int main()
{
    int i,j;
    int m,a,b;
    int flag,sign;
    while(cin >> n >> m)
    {
        sign = 0;
      
        memset(Map,0,sizeof Map );
        memset(indegree,0,sizeof indegree );
        
        if(n == 0 && m == 0)  return 0;
        
        for(i = 0; i < m; i ++)
        {
            cin >> s;
            if(sign == 1)  continue;
            a = s[0] -'A';
            b = s[2] - 'A';
            Map[a][b] = 1;//邻接矩阵存边
            indegree[b] ++;//b的入度加一
            flag =tuopu();//m每给出一条信息,就重新判断,判断当前的已知信息能不能得出结果
            if(flag == -1)//关系矛盾
            {
                cout << "Inconsistency found after "<< i + 1 << " relations." << endl;
                sign = 1;//已经得出了结果
            }
            else if(flag == 1)//得出关系
            {
                cout << "Sorted sequence determined after "<< i + 1 << " relations: ";
                for(j = 0; j < n; j++)
                {
                    printf("%c",Stack[j]+'A');
                }
                printf(".\n");
                sign = 1;
            }
        }
        if(!sign)//当前所给信息不足以得出关系
        {
            cout << "Sorted sequence cannot be determined." << endl;
        }
    }
    return 0;
}

题目2:

题目描述:

给定一张无向图,求图中一个至少包含3个点的环,环上的节点不重复,并且环上的边的长度之和最小。

该问题称为无向图的最小环问题。你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。

输入格式:
第一行包含两个整数N和M,表示无向图有N个点,M条边。

接下来M行,每行包含三个整数u,v,l,表示点u和点v之间有一条边,边长为l。

输出格式:
输出占一行,包含最小环的所有节点(按顺序输出),如果不存在则输出’No solution.’。

数据范围:
1≤N≤100 ,
1≤M≤10000,
1≤l<500

样例:

输入样例:

5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20

输出样例:

1 3 5 2

思路:

1.本题的思路就是考虑最小环里面节点编号最大的节点为k,且环里面与k相连的两个点为i,j,环的长度为g[i][k]+g[k][j]+d[j][i];

2.则d[j][i]则表示j到i且经过的节点编号小于k,因为在环中k就是最大的,只能经过小于k的节点了;

3.则这与floyd中k次循环开始前的d[i][j]意义相同;

4.那就不妨在floyd的第一重循环就求一下以k为最大节点编号的环的长度,注意这里的k必须与节点的意义一样:0-n-1或1-n;

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 110,INF = 0x3f3f3f3f;

int d[N][N],g[N][N];
int pos[N][N];
int path[N],cnt;

void get_path(int i,int j)
{
    if(pos[i][j] == 0) return;//表示pos没有被更新过,即i,j间直达不需要借助节点就最短;
    int k = pos[i][j];
    get_path(i,k);
    path[cnt ++] = k;
    get_path(k,j);
}

int main()
{
    int i,j,k;
    int n,m;
    cin >> n >> m;
    
    memset(g, 0x3f, sizeof g);
    
    for(i = 1 ; i <= n ; i ++)  g[i][i] = 0;
    
    int a,b,c;
    while(m --)
    {
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);
    }
    
    memcpy(d, g, sizeof g);
    
    ll res=INF;
    for(k = 1 ; k <= n ; k ++)//每一次开始本次floyd时,d[i][j]的实际意义是从i到j经过的节点编号最大就是k-1
    {
        for(i = 1 ; i < k ; i ++)//注意i和j的枚举范围
        {
            for(j = i + 1 ; j < k ; j ++)
            {
                 if(g[i][k]+g[k][j]+d[j][i] < res)
                 {
                     res = g[i][k] + g[k][j] + d[j][i];
                     //记录最小环
                     cnt=0;
                     path[cnt ++] = k;
                     path[cnt ++] = i;
                     get_path(i,j);
                     path[cnt ++] = j;
                  }
            }
           
        }

        for(i = 1 ; i <= n ; i ++)
        {
            for(j = 1 ; j <= n ; j ++)
            {
                if(d[i][k] + d[k][j] < d[i][j])
                {
                    d[i][j] = d[i][k] + d[k][j];
                    pos[i][j] = k;//代表i到j的最短距离要紧过节点编号不超过k的若干点,且一定经过k点
                }
            }
        }
    }
    if(res == INF) cout << "No solution." << endl;
    else
    {
        for(i = 0 ; i < cnt ; i ++)  cout << path[i]<<" ";
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

21RGHLY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值