拓扑排序模板
输入初始化,记录有向边和入度
void Init(){
for(int i=1;i<=m;i++){
int t1,t2;
scanf("%d%d",&t1,&t2);
G[t1].push_back(t2);
indegree[t2]++;
}
}
拓扑排序函数
bool topo(){
for(int i=1;i<=n;i++)
if(!indegree[i]) q.push(i); //如果要求字典序,就用优先队列
int cnt = 0;
while(!q.empty()){
cnt++;
int p = q.front(); q.pop();
q2.push(p);
for(int j=0;j<G[p].size();j++){
int v = G[p][j];
indegree[v]--;
if(indegree[v] == 0) q.push(v);
}
}
if(cnt != n) return false; //用于判环
else{
while(!q2.empty()) cout<<q2.front()<<" "; q2.pop();
}
}
模板题:UVA10305&&HDU1285
hdu2647
题解:反向建边,初始度为0的不用给钱val = 0。每一个点的钱为上一个的加1。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 10000 + 10;
int const LOW = 888;
int n,m,val[N];
vector<int>G[N];
int indegree[N];
int topo(){
queue<int>q;
for(int i=1;i<=n;i++)
if(!indegree[i]) q.push(i);
int cnt = 0;
while(!q.empty()){
cnt++;
int p = q.front(); q.pop();
for(int i=0;i<G[p].size();i++){
int v = G[p][i];
indegree[v]--;
if(!indegree[v]) val[v] = val[p] + 1, q.push(v);
}
}
if(cnt != n) return -1;
int sum = 0;
for(int i=1;i<=n;i++)
sum += LOW + val[i];
return sum;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) G[i].clear(), indegree[i] = 0, val[i] = 0;
for(int i=1;i<=m;i++){
int t1,t2;
scanf("%d%d",&t1,&t2); //t1工资要比t2高
G[t2].push_back(t1); //反向建边
indegree[t1]++;
}
printf("%d\n",topo());
}
return 0;
}
POJ1094
题解:这一题WA好多次。看来我对拓扑排序理解不够深刻。
- 如果在循环当中,找到无法判断不要立刻返回,应该做好记录。因为有可能会出现矛盾。矛盾是第一要判断的。
- 入度的数组要拷贝一份。我C语言基础没打好呀!orz。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 30;
vector<int>G[N];
int n,m,in[N],tmp[N];
queue<int>q2;
int topo(int indegree[N]){
queue<int>q;
while(!q2.empty()) q2.pop();
for(int i=1;i<=n;i++)
if(indegree[i] == 0) q.push(i);
int cnt = 0;
bool flag = false; //记录是否无法判断
while(!q.empty()){
if(q.size() > 1) flag = true;
cnt++;
int p = q.front(); q.pop();
q2.push(p);
for(int i=0;i<G[p].size();i++){
int v =G[p][i];
indegree[v]--;
if(indegree[v] == 0) q.push(v);
}
}
if(cnt != n) return -1;//先判断是否矛盾
if(flag) return -2; //再判断是否无法判断
return 1; //找到
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(!m && !n) return 0;
for(int i=1;i<=n;i++) G[i].clear(), in[i] = 0;
int judge = 0;
for(int i=0;i<m;i++){ //每读入一个关系进行以此拓扑排序
char t,t1,t2;
scanf(" %c %c %c",&t1,&t,&t2);
G[t1-'A'+1].push_back(t2-'A'+1);
in[t2-'A'+1]++;
memcpy(tmp,in,sizeof(in)); //拷贝一份
if(!judge){
int flag = topo(tmp);
if(flag == 1){ //如果找到关系可能仅仅上A<B,B<C,但是后面还有C<D
printf("Sorted sequence determined after %d relations: ",i+1);
while(!q2.empty()) cout<<(char)(q2.front()+'A'-1), q2.pop();
printf(".\n");
judge = 1;
}else if(flag == -1){
printf("Inconsistency found after %d relations.\n",i+1);
judge = 1;
}
}
}
if(!judge) printf("Sorted sequence cannot be determined.\n");
}
}
HDU4857
题解:
- 和平常见的拓扑排序不太一样。不是考虑字典序最小,而是数字最小。
- 注意有重边不影响,拓扑排序每次可以把重边的度都减掉。
- 参考https://blog.csdn.net/qq_28954601/article/details/61627321
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 30000 + 10;
int n,m,in[N],cnt;
int ans[N];
vector<int>G[N];
void topo(){
priority_queue<int>q;
cnt = 0;
for(int i=1;i<=n;i++)
if(!in[i]) q.push(i);
while(!q.empty()){
int p = q.top(); q.pop();
ans[++cnt] = p;
for(int i=0;i<G[p].size();i++)
if(--in[G[p][i]] == 0) q.push(G[p][i]);
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) G[i].clear();
memset(in,0,sizeof(in));
for(int i=1;i<=m;i++){ //有重复的边
int a,b; scanf("%d%d",&a,&b);
G[b].push_back(a); ++in[a];
}
topo();
printf("%d",ans[n]);
for(int i=n-1;i>=1;i--) printf(" %d",ans[i]);
printf("\n");
}
return 0;
}