TAKMIČENJA IZ PROGRAMIRANJA


Sva pitanja, predloge ili primedbe u vezi sa takmičenjima iz programiranja možete slati na e-mail:

tak.prog@gmail.com

U toku perioda za žalbe, sve žalbe možete slati na ovaj isti e-mail.

asfalt.cpp    1,828 b      izvorni kod rešenja
asfalt.pas    2,918 b      izvorni kod rešenja
asfalt.tests.7z    5,619,190 b      test primeri
asfalt.checker.cpp    859 b      program za testiranje

zadatak: Asfalt

U jednoj državi postoji n gradovo i m dvosmernih auto-puteva između nekih od njih. Svaki grad ima neku vrednost v iz skupa {1, 2, ..., n}, pri čemu ne postoje dva grada sa istom vrednošću. Ove vrednosti predstavljaju indekse popularnosti i grad sa vrednošću n je gravni grad.

Odlučeno je da neke od puteva u ovoj državi treba asfaltirati, pri čemu će asfaltiranje finansirati gradovi. Svaki grad je rekao da će asfaltirati (tačno jedan) put koji vodi od njega do njegovog suseda sa najvećom vrednošću (jer je to u interesu svakog grada). Međutim, ukoliko je vrednost nekog grada veća od svih njegovih suseda on ne asfaltira nijedan put.

Mi smo stranci i ne znamo vrednosti gradova, ali imamo mapu i zbamo koji su putevi asfaltirani. Ukoliko ima tačno n - 1 asfaltiranih puteva, odrediti sve gradove koji mogu biti glavni. Garantuje se da postoji bar jedan takav grad, tj. mapa je zadata korektno.

Ulaz:

(Ulazni podaci se učitavaju iz datoteke asfalt.in.) U prvom redu ulaza nalaze se 2 prirodna broja n i m, koji predstavljaju, redom, broj gradova i broj auto-puteva ( n ≤ 20.000, m≤ 200.000). U narednih m redova sledi opis mape: po tri broja ai, bi i ci u svakom redu koji očnačavaju da postoji (neusmeren) put između gradova ai i bi, pri čemu je taj put asfaltiran ako je ci = 0. Gradovi su numerisani od 1 do n i između svaka dva grada postoji najviše jedan auto-put.

Izlaz:

(Izlazni podaci se ispisuju u datoteku asfalt.out.) U prvom redu izlaza ispisati broj gradova koji su kandidati za glavni grad. U sledećem redu ispisati redne brojeve tih gradova u proizvoljnom redosledu,odvojene razmakom.

Primer:

asfalt.in      asfalt.out
4 4
1 2 1
2 3 1
3 4 1
4 1 0
        
2
2 3

Objašnjenje.

Asfaltirani putevi su: 1-2, 2-3 i 3-4. Ukoliko bi grad 1 bio glavni grad onda bi sused grada 4 koji ima najveću vrednost bio baš grad 1 i grad 4 bi asfaltirao put 4-1. Međutim, ovaj put nije asfaltiran pa grad 1 ne može biti glavni. Slično i za grad 4. Gradovi 2 i 3 mogu biti glavni, npr. za v(1) = 1, v(2) = 4, v(3) = 3 i v(4) = 2, grad 2 postaje glavni i asfaltiranje je korektno. Slično i za grad 3.

Napomena.

U 30% test primera je n≤ 200 a u 50% test primera n≤ 2.000.

fajl: asfalt.cpp
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <memory.h>

using namespace std;

const int MaxN = 20200;
const int MaxM = 200200;

struct edge {
  int v;
  bool asfalt;
  edge(int x, bool y) { v = x; asfalt = y; }; 
};

int dfs[MaxN], currSon[MaxN], city[MaxN];
vector<edge> adj[MaxN];
bool mark[MaxN];
int n, m, time, root, sol, a, b, c;

void DFS(int u) {
  int v;

  time++;
  dfs[u] = time;

  for (int i = 0; i < adj[u].size(); i++) {
    v = adj[u][i].v;
    
    // ako je u-v tree-edge i cvor v nije obidjen
    if (adj[u][i].asfalt && dfs[v] == 0) {
      currSon[u] = v;
      DFS(v);
    }
    // ako u-v nije tree-edge i cvor v je vec obidjen
    if (!adj[u][i].asfalt && dfs[v] != 0) {

      //tada je u-v back ili "cross" edge i markiramo krajeve
      mark[u] = true;
      mark[v] = true;

      //ako je u-v back edge, proverimo da li nam treba novi root
      if (currSon[v] != 0 && dfs[ currSon[v] ] > dfs[root])
        root = currSon[v];
    }
  }

  currSon[u] = 0;
}

void DFS2(int u) {
  int v;

  mark[u] = true;
  city[sol++] = u;

  for (int i = 0; i < adj[u].size(); i++) {
    v = adj[u][i].v;
    if (adj[u][i].asfalt && !mark[v])
      DFS2(v);
  }
}

int main() {

  FILE* inFile = fopen("asfalt.in", "r");
  FILE* outFile = fopen("asfalt.out", "w");

  fscanf(inFile, "%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    fscanf(inFile, "%d%d%d", &a, &b, &c);
    adj[a].push_back(edge(b, c == 1));
    adj[b].push_back(edge(a, c == 1));
  }

  memset(dfs, 0, sizeof(dfs));
  memset(currSon, 0, sizeof(currSon));
  memset(mark, false, sizeof(mark));
  root = 1;  time = 0;  sol = 0;

  DFS(1);
  DFS2(root);

  fprintf(outFile, "%d\n", sol);
  for (int i = 0; i < sol - 1; i++)
    fprintf(outFile, "%d ", city[i]);
  fprintf(outFile, "%d\n", city[sol - 1]);

  return 0;
}
fajl: asfalt.pas
const
  MaxN = 20020;
  MaxM = 200020;


var
   inFile, outFile: Text;
   n, m, time, root, sol, a, b, c : longint;
   dfsNum, currSon, city, p, deg : array[0..MaxN] of longint;
   a1, b1, c1, adj, asfalt : array[0..2*MaxM] of longint;
   mark : array[0..MaxN] of boolean;



procedure Input;
var
   i, a, b, c: longint;

begin

   assign( inFile, 'asfalt.in' );
   reset( inFile );
   fillchar( deg, sizeof( deg ), 0 );

   readln( inFile, n, m );
   for i := 1 to m do begin
      readln( inFile, a, b, c );
      deg[ a ] := deg[ a ] + 1;
      deg[ b ] := deg[ b ] + 1;
      a1[ i ] := a; b1[ i ] := b; c1[ i ] := c;
      a1[ i + m ] := b; b1[ i + m ] := a; c1[ i + m] := c;
   end;

   close( inFile );

end;



procedure Output;
var
  i : longint;

begin

   assign( outFile, 'asfalt.out' );
   rewrite( outFile );
   writeln( outFile, sol );
   for i := 1 to sol - 1 do
      write( outFile, city[ i ], ' ' );
   writeln( outFile, city[ sol ] );
   close( outFile);

end;



(* simulacija liste preko counting sorta *)
procedure ListSimulation;
var
   i: longint;

begin

   p[0] := 0;
   for i := 1 to n do
      p[ i ] := p[ i - 1 ] + deg[ i - 1 ];

   for i := 1 to 2 * m do begin
      adj[ p[ a1[ i ] ] ] := b1[ i ];
      asfalt[ p[ a1[ i ] ] ] := c1[ i ];
      p[ a1[ i ] ] := p[ a1[ i ] ] + 1;
   end;

   for i := 1 to n do
      p[ i ] := p[ i - 1 ] + deg[ i - 1 ];

end;



procedure DFS( u : longint );
var
   v, i : longint;

begin

   time := time + 1;
   dfsNum[ u ] := time;

   for i := p[ u ] to p[ u ] + deg[ u ] - 1 do begin
      v := adj[ i ];


      (* ako je u-v tree-edge i cvor v nije obidjen *)
      if ( (asfalt[ i ] = 1) AND (dfsNum[ v ] = 0) ) then begin
         currSon[ u ] := v;
         DFS( V );
      end;

      (* ako u-v nije tree-edge i cvor v je vec obidjen *)
      if ( (asfalt[ i ] = 0) AND (dfsNum[ v ] <> 0) ) then begin

         (* tada je u-v back ili "cross" edge i markiramo krajeve *)
   mark[ u ] := true;
   mark[ v ] := true;

   (* ako je u-v back edge, proverimo da li nam treba novi root *)
   if ( (currSon[ v ] <> 0) AND (dfsNum[ currSon[ v ] ] > dfsNum[ root ]) ) then
      root := currSon[ v ];

      end;

   end;

   currSon[ u ] := 0;

end;



procedure DFS2( u : longint );
var
   v, i : longint;

begin

   mark[ u ] := true;
   sol := sol + 1;
   city[ sol ] := u;

   for i := p[ u ] to p[ u ] + deg[ u ] - 1 do begin
      v := adj[ i ];
      if ( (asfalt[ i ] = 1) AND (NOT mark[ v ]) ) then
         DFS2(v);
   end;

end;



begin

   Input;
   ListSimulation;

   fillchar( dfsNum, sizeof( dfsNum ), 0 );
   fillchar( currSon, sizeof( currSon ), 0);
   fillchar( mark, sizeof( mark ), false );
   root := 1;  time := 0;  sol := 0;

   DFS(1);
   DFS2(root);

   Output;

end.