Sortare topologica O sortare topologica a varfurilor unui graf orientat aciclic este o operatie de ordonare liniara a varfurilor, astfel incat, daca exista un arc ( i, j ), atunci i apare inaintea lui j in aceasta ordonare. Date de intrare In fisierul de intrare sortaret.in vom avea pe prima linie doua numere intregi N si M. Pe fiecare dintre urmatoarele M linii se vor afla cate doua numere intregi, separate intre ele printr-un spatiu, X si Y, Y, cu semnificatia ca exista arc de la nodul X catre cat re nodul Y. Y. Date de iesire Fisierul de iesire sortaret.out va contine pe o singura linie N numere separate intre ele prin spatii, care reprezinta sortarea topologica a nodurilor grafului dat. Daca exista mai multe solutii se va afisa oricare. Restrictii 1 ≤ N ≤ 50000 1 ≤ M ≤ 100000 Pot exista mai multe arce intre doua noduri X si Y Exemplusortaret.insortaret.out 98 12 13 34 35 59 46 47 48 123467859 Indicatii de rezolvare O scurta prezentare a acestui subiect gasiti aici Algoritmul de Sortare Topologica Topologica il gasiti foarte bine explicat si in cartea Introducere in algoritmi, Thomas Cormen, editura Agora, Cluj-Napoca. O idee de rezolvare este sa introducem, pe rand, intr-o lista, nodurile care la un moment dat un gradul exterior zero. Odata ce un nod este introdus in lista, vom scoate nodul respectiv din graf si vom considera in continuare graful ramas. O implementare directa are complexitatea O(N2) si se gaseste aici. #include
#include #include using namespace std; #define MAXN 50100 #define pb push_back int N, M, viz[MAXN], deg[MAXN]; vector G[MAXN]; void solve_and_write(void) { int i, j, k; for(i = 1; i <= N; i++) { for(j = 1; j <= N; j++) if(!viz[j] && deg[j] == 0) { viz[j] = 1, printf("%d ", j); for(k = 0; k < G[j].size(); k++) deg[ G[j][k] ]--; break ; } } } void read_data(void) { int i, a, b; scanf("%d %d\n", &N, &M); for(i = 1; i <= M; i++) scanf("%d %d", &a, &b), G[a].pb(b), deg[b]++; } int main(void) { freopen("sortaret.in", "rt", stdin); freopen("sortaret.out", "wt", stdout); read_data(); solve_and_write(); return 0; } Daca rafinam aceasta idee, introducand succesiv nodurile intr-o coada, putem obtine complexitatea O(N+M), sursa se gaseste aici.
#include #include #include using namespace std; #define MAXN 50100 #define pb push_back int N, M, deg[MAXN], Q[MAXN]; vector G[MAXN]; // deg[x] = gradul exterior al nodului x // folosesc o coada Q in care introduc pe rand nodurile care // au gradul exterior zero // complexitate O(N+M) void solve(void) { int i, x; vector::iterator it; for(x = 1; x <= N; x++) if(deg[x] == 0) Q[++Q[0]] = x; for(i = 1; i <= N; i++) { x = Q[i]; for(it = G[x].begin(); it != G[x].end(); ++it) { deg[*it]--; if(deg[*it] == 0) Q[++Q[0]] = *it; } } } void read_data(void) { int i, a, b; scanf("%d %d\n", &N, &M); for(i = 1; i <= M; i++) scanf("%d %d\n", &a, &b), G[a].pb(b), deg[b]++; } void write_data(void) { int i; for(i = 1; i <= N; i++) printf("%d ", Q[i]); } int main(void)
{ freopen("sortaret.in", "rt", stdin); freopen("sortaret.out", "wt", stdout); read_data(); solve(); write_data(); return 0; } O alta posibila idee de rezolvare consta intr-o parcurgere in adancime pentru a calcula timpii de terminare pentru fiecare varf v. Pe masura ce fiecare varf este terminat, este inserat in capul unei liste simplu inlantuite. Parcurgerea listei va constitui solutia. Acest algoritm are o complexitate de O(N+M) deoarece cautarea in adancime necesita un timp O(N+M) iar inserarea fiecaruia din cele |N| varfuri in capul liste simplu inlantuite necesita timp O(1). Sursa se gaseste aici. #include #define ALB 0 #define GRI 1 #define NEGRU 2 #define NMAX 50005 typedef struct nod { int vf; nod * next; } *PNOD, NOD; PNOD L[NMAX];//Listele de vecini pentru fiecare nod PNOD adresa; // lista simplu inlantuita int color[NMAX]; int N, M; void Read(); void DF(int); void Push(int); void S_Topologica(); void Add( int,int); void Write(); int main() { Read(); S_Topologica(); Write(); return 0;
} void Read() { freopen( "sortaret.in" , "r", stdin ); scanf( "%d%d", &N, &M ); int X, Y; for ( ; M > 0; M-- ) { scanf( "%d%d", &X, &Y); Add(X,Y); } } void Add( int i, int j) { PNOD p = new NOD; p->vf = j; p->next = L[i]; L[i] = p; } void S_Topologica() { int i; for ( i = 1; i <= N; ++i ) if ( color[i] == ALB ) DF( i ); } void DF( int nod ) { color[nod] = GRI; for ( PNOD p = L[nod]; p; p = p->next ) if ( color[p->vf] == ALB ) DF( p->vf ); color[nod] = NEGRU; Push( nod ); } void Push( int nod ) { PNOD p = new NOD; p->vf = nod; p->next = adresa;
adresa = p; } void Write() { freopen( "sortaret.out", "w", stdout ); for ( PNOD p = adresa; p; p = p->next ) printf( "%d ", p->vf ); }
Rezolvare gasita de mine: #include #include using namespace std; int n, m, grad[100000], coada[100000]; vector vecini[50100]; void rezolvare() { int x; coada[0]=0; for (x=1;x<=n;x++) if (grad[x]==0) coada[++coada[0]]=x; for(int i=1;i<=n;i++) { x=coada[i]; for(vector ::iterator it=vecini[x].begin();it!=vecini[x].end();it++) { --grad[*it]; if (grad[*it]==0) coada[++coada[0]]=*it; } } } void citire() { int a,b; // cout<<"n,m\n"; scanf("%d %d",&n,&m);
/*for (int i=1;i<=5000;i++) { vecini[i][0]=0; grad[i]=0; } */ for (int i=1;i<=m;i++) { // cout<<"a,b="<<"\n"; scanf("%d %d", &a, &b); vecini[a].push_back(b); grad[b]++; } } void afisare() { for(int i=1;i<=n;i++) printf("%d ",coada[i]); } int main() { freopen("sortaret.in","r",stdin); freopen("sortaret.out","w",stdout); citire(); rezolvare(); afisare(); return 0; }