字母排序(关于拓扑排序的一些细节)

P1682

#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define a(i,j) a[i-96][j-96]//用宏定义简洁一些
#define rd(i) rd[i-96]
#define mark(i) mark[i-96]
int n,m;
char s1[25],s2[25],s[1005];
int a[30][30],rd[30],mark[30];
map<char,char>mp;
void mycmp(char *s1,char *s2)
{
    int len=min(strlen(s1),strlen(s2));//这里是min,不然会异常退出
    for(int i=0;i<len;i++)
    {
        if(s1[i]!=s2[i])
        {
            //cout<<s1[i]<<"<"<<s2[i]<<endl;
            if(a(s1[i],s2[i]))break;
            //这里有一个区分:
            /*
            在邻接矩阵中是需要排除重复的,因为只计算了一条边而入度却增加了
            相反在兄弟储存法里面就不需要,因为 
            */ 
            a(s1[i],s2[i])=1;
            mark(s1[i])=1;
            mark(s2[i])=1;
            rd(s2[i])++;
            return;
        }
    }
}
void Init()
{
    scanf("%d%d",&n,&m);
    scanf("%s",s1);
    for(int i=2;i<=m;i++)
    {
        scanf("%s",s2);
        mycmp(s1,s2);
        strcpy(s1,s2);
    }
    /*for(int i=1;i<=26;i++)
    {
        cout<<mark[i]<<rd[i]<<endl;
    }
    cout<<endl;*/
}
vector<int>topo;
bool toposort()
{
    queue<int>q;
    for(int i=1;i<=26;i++)
        if(!rd[i]&&mark[i])q.push(i);//邻接矩阵不能保证所有访问的节点都是已经定义的,因此必须加判断
    while(!q.empty())
    {
        if(q.size()>1)return 0;
        int i=q.front();q.pop();
        topo.push_back(i);
        for(int p=1;p<=26;p++)
        {
            if(!a[i][p])continue;
            rd[p]--;
            if(!rd[p])q.push(p);
        }
    }
//  cout<<topo.size()<<endl;
    if(topo.size()==n)
        return 1;
    return 0;
}
void out()
{
    for(int i=0;i<n;i++)
    {
        char c=topo[i]+96;
        mp[c]=i+97;//这里的映射倒是很好用
    }
    scanf("%s",s);
    int len=strlen(s);
    for(int i=0;i<len;i++)
        printf("%c",mp[s[i]]);
}
int main()
{
    Init();
    if(toposort())
    {
        out();
    }
    else cout<<-1;
    return 0;
}

总结

关于拓扑排序

1、正常的BFS有三种返回关系,其中当q的元素大于1的时候不能直接返回多解,因为它有可能无解
2、DFS不好写,但是可以直接判断环

邻接矩阵

1、要标记节点是否已经被定义
2、要避免重复操作(本来是不影响的,就怕边数目没改更改了其他数据)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值