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、要避免重复操作(本来是不影响的,就怕边数目没改更改了其他数据)