SRM622D1Med EtherNet

これが450ptじゃないの不思議

問題概要(原文)

$N$ 頂点のツリーグラフが与えられる.辺は長さを持つ. 辺を爆破して,どの点どうしの最短距離も $d$ 以下であるようにしたい.何本爆破する必要があるか?

考察

$u$ を根とする部分木に関するグラフを考える.親から最も遠い頂点への距離 $x$ とする.

$u$ の子 $v$ を根とする部分木とのマージを考える.このとき, 親から最も遠い頂点への距離を $y$ とし, $u,v$ をつなぐ辺の長さを $z$ とする.

$x+y+z \leq d$ ならば直径は $d$ 以下なので切らなくてもよい(切ってもよい).そうでないならば必ずこの辺は切らないといけない.

これを全ての頂点を根として試してやる.計算量は $O(N^{2} d^{2} )$ だが,若干の枝刈りでかなりの高速化ができる.

ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Debug = System.Diagnostics.Debug;
using StringBuilder = System.Text.StringBuilder;
public class Ethernet
{
    public int connect(int[] parent, int[] dist, int d)
    {
        var n = parent.Length + 1;
        var G = Enumerate(n, x => new List<KeyValuePair<int, int>>());
        for (int i = 0; i < n - 1; i++)
        {
            G[parent[i]].Add(new KeyValuePair<int, int>(i + 1, dist[i]));
            G[i + 1].Add(new KeyValuePair<int, int>(parent[i], dist[i]));
        }
        Func<int, int, int[]> dfs = null;
        dfs = (prev, cur) =>
          {
              var ret = new int[d + 1];
              for (int i = 1; i <= d; i++)
                  ret[i] = 100000;
              foreach (var e in G[cur])
              {
                  if (e.Key == prev) continue;
                  var res = dfs(cur, e.Key);
                  var next = new int[d + 1];
                  for (int i = 0; i <= d; i++)
                      next[i] = 100000;
                  for (int i = 0; i <= d; i++)
                  {
                      if (ret[i] >= 100) continue;
                      for (int j = 0; j <= d; j++)
                      {
                          if (res[j] >= 100) continue;
                          if (i + j + e.Value > d)
                              next[i] = Math.Min(next[i], ret[i] + res[j] + 1);
                          else
                          {
                              next[Math.Max(i, j + e.Value)] = Math.Min(next[Math.Max(i, j + e.Value)], ret[i] + res[j]);
                              next[i] = Math.Min(next[i], ret[i] + res[j] + 1);
                          }
                      }
                  }
                  ret = next;
              }
              return ret;
          };
        var min = 100000;
        for (int i = 0; i < n; i++)
            min = Math.Min(min, dfs(-1, i).Min());
        return min + 1;
    }
}

コメント

  • これは本当に典型だと思う