Server#10456

Server (Editoryal)

Graf 10^4'e kadar köşe ve 4 \cdot 10^4'e kadar kenar içerir, bu yüzden onu komşuluk listesi şeklinde saklayacağız. Tüm kenar ağırlıkları pozitiftir, dolayısıyla en kısa mesafeler Dijkstra algoritması ile bulunabilir.

Problemin graf terimleriyle ifade edilmesi. ve köşeleri arasındaki en kısa yolun uzunluğunu ile gösterelim. Eğer sunucu şehrinde duruyorsa, c_i şehrinden gelen misafir d(c_i, v) gecikmesini alır. Tüm misafirlerin toplam gecikmesi şuna eşittir

S(v) = d(c_1, v) + d(c_2, v) + ... + d(c_k, v)

değeri minimum olan köşeyi bulmak gerekir, eşitlik durumunda ise en küçük numaralı olanı.

Neden Dijkstra'yı misafirlerin şehirlerinden başlatıyoruz. Her aday köşesi için 'den Dijkstra çalıştırıp misafir şehrine olan mesafeleri toplayabilirdik. Ama aday sayısı 10^4'e kadar olabilir ve bu kadar çok çalıştırma fazla olur. Graf yönsüz olduğundan d(c_i, v) = d(v, c_i) olduğuna dikkat edelim. Demek ki, c_i misafir şehrinden tek bir Dijkstra çalıştırması, sunucunun olası tüm yerleri için bu misafirin gecikmesini hemen verir. Misafir sayısı en fazla olduğundan, çalıştırma yeterlidir.

Toplamın nasıl biriktirildiği. Başlangıçta sıfırlarla doldurulmuş dizisini oluşturalım. elemanı, sunucu köşesine yerleştirildiğinde misafirlerin toplam gecikmesini saklayacaktır.

Sonra her misafir için sırayla:

  1. c_i köşesinden Dijkstra algoritmasını çalıştırıp dist[1..n] dizisini elde ederiz, burada dist[v] = d(c_i, v). Bu, sunucu şehrindeyse -inci misafirin gecikmesidir.

  2. Tüm v = 1, 2, ..., n köşelerini geziyoruz ve sum[v] += dist[v] işlemini gerçekleştiriyoruz. Böylece -inci misafirin katkısını sunucunun olası her yerleşim yerinin toplam gecikmesine ekliyoruz.

misafirin tamamı işlendikten sonra, her elemanı tam olarak terim içerir — köşesine kadar olan tüm misafirlerin gecikmeleri, yani sum[v] = S(v).

Cevabın seçilmesi. Köşeleri v = 1, 2, ..., n artan numara sırasıyla geziyoruz ve minimum toplama sahip köşeyi hatırlıyoruz. Cevabı yalnızca kesin eşitsizlik sum[v] < best sağlandığında güncelliyoruz. Bu durumda aynı minimum toplama sahip birkaç köşe olduğunda, bunlardan ilki, yani en küçük numaralı olanı kalacaktır.

Minimum, yalnızca misafirlerin şehirleri arasında değil, tüm köşe arasında aranır: en uygun yer, misafir bulunmayan bir şehir olabilir.

Örnek

Örnekte verilen graf aşağıdaki gibidir:

Misafirlerin yaşadığı köşelerden Dijkstra algoritmasını çalıştıralım: ve . Bu köşelerin her biri için diğer tüm şehirlere olan en kısa mesafeleri belirleyelim.

Sonra grafın her köşesi için misafirlerin bulunduğu tüm şehirlere olan mesafelerin toplamını bulalım. Minimum mesafe toplamı 'e eşittir ve bu, en küçük numaralı köşesinde elde edilir.

Böylece sunucu numaralı şehre yerleştirilmelidir. Ondan misafirlerin yaşadığı şehirlere olan toplam mesafe 5 + 0 + 8 = 13 olur.

Algoritmanın uygulanması

Sonsuzluk sabitini tanımlayalım.

#define INF 0x3F3F3F3F

yapısı, kenar hakkındaki bilgiyi saklayacaktır: kenarın gittiği köşesi ve bu kenarın ağırlığı.

struct edge
{
  int node, dist;
  edge(int node, int dist) : node(node), dist(dist) {}
};

(node, dist) çiftlerini değerinin azalan sırasına göre sıralamak için bir karşılaştırıcı tanımlayalım.

bool operator< (edge a, edge b)
{
  return a.dist > b.dist;
}

Grafı temsil etmek için komşuluk listesini tanımlayalım.

vector<vector<edge> > g;

Dijkstra fonksiyonu Dijkstra algoritmasını gerçekleştirir. Girdi olarak grafını ve başlangıç köşesini alır. Dönüş değeri dizisidir, burada , köşesinden köşesine olan en kısa mesafeyi içerir.

void Dijkstra(vector<vector<edge> >& g, vector<int>& d, int start)
{
  priority_queue<edge> pq;
  pq.push(edge(start, 0));

  d = vector<int>(n + 1, INF);
  d[start] = 0;

  while (!pq.empty())
  {
    edge e = pq.top(); pq.pop();
    int v = e.node;

    if (e.dist > d[v]) continue;

    for (auto e : g[v])
    {
      int to = e.node;
      int cost = e.dist;
      if (d[v] + cost < d[to])
      {
        d[to] = d[v] + cost;
        pq.push(edge(to, d[to]));
      }
    }
  }
}

Programın ana kısmı. Girdi verilerini okuyoruz.

scanf("%d %d %d", &n, &m, &k);
c.resize(k);
for (i = 0; i < k; i++)
  scanf("%d", &c[i]);

Girdi ağırlıklı grafını okuyoruz.

g.resize(n + 1);
for (i = 0; i < m; i++)
{
  scanf("%d %d %d", &a, &b, &w);
  g[a].push_back(edge(b, w));
  g[b].push_back(edge(a, w));
}

Misafirlerin yaşadığı şehirlerden Dijkstra algoritmasını çalıştırıyoruz.

dp.resize(n + 1);
for (i = 0; i < k; i++)
{
  Dijkstra(g, dist, c[i]);

  for (j = 1; j <= n; j++)
   dp[j] += dist[j];
}

Şu anda , köşesinden misafirlerin bulunduğu tüm şehirlere olan en kısa mesafelerin toplamını içermektedir. Geriye dizisinin elemanları arasında minimum değeri bulmak ve buna karşılık gelen cevabı yazdırmak kalıyor. değişkeni, sunucunun yerleştirilmesi gereken en küçük köşe numarasını saklar.

res = INF;
for (i = 1; i <= n; i++)
{
  if (dp[i] < res)
  {
    res = dp[i];
    ptr = i;
  }
}

Cevabı yazdırıyoruz.

printf("%d %d\n", ptr, dp[ptr]);